diff options
Diffstat (limited to 'security/keys')
25 files changed, 5924 insertions, 1997 deletions
diff --git a/security/keys/Kconfig b/security/keys/Kconfig new file mode 100644 index 00000000000..a4f3f8c48d6 --- /dev/null +++ b/security/keys/Kconfig @@ -0,0 +1,100 @@ +# +# Key management configuration +# + +config KEYS +	bool "Enable access key retention support" +	select ASSOCIATIVE_ARRAY +	help +	  This option provides support for retaining authentication tokens and +	  access keys in the kernel. + +	  It also includes provision of methods by which such keys might be +	  associated with a process so that network filesystems, encryption +	  support and the like can find them. + +	  Furthermore, a special type of key is available that acts as keyring: +	  a searchable sequence of keys. Each process is equipped with access +	  to five standard keyrings: UID-specific, GID-specific, session, +	  process and thread. + +	  If you are unsure as to whether this is required, answer N. + +config PERSISTENT_KEYRINGS +	bool "Enable register of persistent per-UID keyrings" +	depends on KEYS +	help +	  This option provides a register of persistent per-UID keyrings, +	  primarily aimed at Kerberos key storage.  The keyrings are persistent +	  in the sense that they stay around after all processes of that UID +	  have exited, not that they survive the machine being rebooted. + +	  A particular keyring may be accessed by either the user whose keyring +	  it is or by a process with administrative privileges.  The active +	  LSMs gets to rule on which admin-level processes get to access the +	  cache. + +	  Keyrings are created and added into the register upon demand and get +	  removed if they expire (a default timeout is set upon creation). + +config BIG_KEYS +	bool "Large payload keys" +	depends on KEYS +	depends on TMPFS +	help +	  This option provides support for holding large keys within the kernel +	  (for example Kerberos ticket caches).  The data may be stored out to +	  swapspace by tmpfs. + +	  If you are unsure as to whether this is required, answer N. + +config TRUSTED_KEYS +	tristate "TRUSTED KEYS" +	depends on KEYS && TCG_TPM +	select CRYPTO +	select CRYPTO_HMAC +	select CRYPTO_SHA1 +	help +	  This option provides support for creating, sealing, and unsealing +	  keys in the kernel. Trusted keys are random number symmetric keys, +	  generated and RSA-sealed by the TPM. The TPM only unseals the keys, +	  if the boot PCRs and other criteria match.  Userspace will only ever +	  see encrypted blobs. + +	  If you are unsure as to whether this is required, answer N. + +config ENCRYPTED_KEYS +	tristate "ENCRYPTED KEYS" +	depends on KEYS +	select CRYPTO +	select CRYPTO_HMAC +	select CRYPTO_AES +	select CRYPTO_CBC +	select CRYPTO_SHA256 +	select CRYPTO_RNG +	help +	  This option provides support for create/encrypting/decrypting keys +	  in the kernel.  Encrypted keys are kernel generated random numbers, +	  which are encrypted/decrypted with a 'master' symmetric key. The +	  'master' key can be either a trusted-key or user-key type. +	  Userspace only ever sees/stores encrypted blobs. + +	  If you are unsure as to whether this is required, answer N. + +config KEYS_DEBUG_PROC_KEYS +	bool "Enable the /proc/keys file by which keys may be viewed" +	depends on KEYS +	help +	  This option turns on support for the /proc/keys file - through which +	  can be listed all the keys on the system that are viewable by the +	  reading process. + +	  The only keys included in the list are those that grant View +	  permission to the reading process whether or not it possesses them. +	  Note that LSM security checks are still performed, and may further +	  filter out keys that the current process is not authorised to view. + +	  Only key attributes are listed here; key payloads are not included in +	  the resulting table. + +	  If you are unsure as to whether this is required, answer N. diff --git a/security/keys/Makefile b/security/keys/Makefile index 74d5447d7df..dfb3a7beded 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -2,6 +2,9 @@  # Makefile for key management  # +# +# Core +#  obj-y := \  	gc.o \  	key.o \ @@ -12,7 +15,14 @@ obj-y := \  	request_key.o \  	request_key_auth.o \  	user_defined.o -  obj-$(CONFIG_KEYS_COMPAT) += compat.o  obj-$(CONFIG_PROC_FS) += proc.o  obj-$(CONFIG_SYSCTL) += sysctl.o +obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o + +# +# Key types +# +obj-$(CONFIG_BIG_KEYS) += big_key.o +obj-$(CONFIG_TRUSTED_KEYS) += trusted.o +obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ diff --git a/security/keys/big_key.c b/security/keys/big_key.c new file mode 100644 index 00000000000..8137b27d641 --- /dev/null +++ b/security/keys/big_key.c @@ -0,0 +1,207 @@ +/* Large capacity key type + * + * Copyright (C) 2013 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 Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/seq_file.h> +#include <linux/file.h> +#include <linux/shmem_fs.h> +#include <linux/err.h> +#include <keys/user-type.h> +#include <keys/big_key-type.h> + +MODULE_LICENSE("GPL"); + +/* + * If the data is under this limit, there's no point creating a shm file to + * hold it as the permanently resident metadata for the shmem fs will be at + * least as large as the data. + */ +#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) + +/* + * big_key defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +struct key_type key_type_big_key = { +	.name			= "big_key", +	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT, +	.instantiate		= big_key_instantiate, +	.match			= user_match, +	.revoke			= big_key_revoke, +	.destroy		= big_key_destroy, +	.describe		= big_key_describe, +	.read			= big_key_read, +}; + +/* + * Instantiate a big key + */ +int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ +	struct path *path = (struct path *)&key->payload.data2; +	struct file *file; +	ssize_t written; +	size_t datalen = prep->datalen; +	int ret; + +	ret = -EINVAL; +	if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) +		goto error; + +	/* Set an arbitrary quota */ +	ret = key_payload_reserve(key, 16); +	if (ret < 0) +		goto error; + +	key->type_data.x[1] = datalen; + +	if (datalen > BIG_KEY_FILE_THRESHOLD) { +		/* Create a shmem file to store the data in.  This will permit the data +		 * to be swapped out if needed. +		 * +		 * TODO: Encrypt the stored data with a temporary key. +		 */ +		file = shmem_kernel_file_setup("", datalen, 0); +		if (IS_ERR(file)) { +			ret = PTR_ERR(file); +			goto err_quota; +		} + +		written = kernel_write(file, prep->data, prep->datalen, 0); +		if (written != datalen) { +			ret = written; +			if (written >= 0) +				ret = -ENOMEM; +			goto err_fput; +		} + +		/* Pin the mount and dentry to the key so that we can open it again +		 * later +		 */ +		*path = file->f_path; +		path_get(path); +		fput(file); +	} else { +		/* Just store the data in a buffer */ +		void *data = kmalloc(datalen, GFP_KERNEL); +		if (!data) { +			ret = -ENOMEM; +			goto err_quota; +		} + +		key->payload.data = memcpy(data, prep->data, prep->datalen); +	} +	return 0; + +err_fput: +	fput(file); +err_quota: +	key_payload_reserve(key, 0); +error: +	return ret; +} + +/* + * dispose of the links from a revoked keyring + * - called with the key sem write-locked + */ +void big_key_revoke(struct key *key) +{ +	struct path *path = (struct path *)&key->payload.data2; + +	/* clear the quota */ +	key_payload_reserve(key, 0); +	if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) +		vfs_truncate(path, 0); +} + +/* + * dispose of the data dangling from the corpse of a big_key key + */ +void big_key_destroy(struct key *key) +{ +	if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) { +		struct path *path = (struct path *)&key->payload.data2; +		path_put(path); +		path->mnt = NULL; +		path->dentry = NULL; +	} else { +		kfree(key->payload.data); +		key->payload.data = NULL; +	} +} + +/* + * describe the big_key key + */ +void big_key_describe(const struct key *key, struct seq_file *m) +{ +	unsigned long datalen = key->type_data.x[1]; + +	seq_puts(m, key->description); + +	if (key_is_instantiated(key)) +		seq_printf(m, ": %lu [%s]", +			   datalen, +			   datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); +} + +/* + * read the key data + * - the key's semaphore is read-locked + */ +long big_key_read(const struct key *key, char __user *buffer, size_t buflen) +{ +	unsigned long datalen = key->type_data.x[1]; +	long ret; + +	if (!buffer || buflen < datalen) +		return datalen; + +	if (datalen > BIG_KEY_FILE_THRESHOLD) { +		struct path *path = (struct path *)&key->payload.data2; +		struct file *file; +		loff_t pos; + +		file = dentry_open(path, O_RDONLY, current_cred()); +		if (IS_ERR(file)) +			return PTR_ERR(file); + +		pos = 0; +		ret = vfs_read(file, buffer, datalen, &pos); +		fput(file); +		if (ret >= 0 && ret != datalen) +			ret = -EIO; +	} else { +		ret = datalen; +		if (copy_to_user(buffer, key->payload.data, datalen) != 0) +			ret = -EFAULT; +	} + +	return ret; +} + +/* + * Module stuff + */ +static int __init big_key_init(void) +{ +	return register_key_type(&key_type_big_key); +} + +static void __exit big_key_cleanup(void) +{ +	unregister_key_type(&key_type_big_key); +} + +module_init(big_key_init); +module_exit(big_key_cleanup); diff --git a/security/keys/compat.c b/security/keys/compat.c index 792c0a611a6..347896548ad 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -1,4 +1,4 @@ -/* compat.c: 32-bit compatibility syscall for 64-bit systems +/* 32-bit compatibility syscall for 64-bit systems   *   * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -12,18 +12,61 @@  #include <linux/syscalls.h>  #include <linux/keyctl.h>  #include <linux/compat.h> +#include <linux/slab.h>  #include "internal.h" -/*****************************************************************************/  /* - * the key control system call, 32-bit compatibility version for 64-bit archs - * - this should only be called if the 64-bit arch uses weird pointers in - *   32-bit mode or doesn't guarantee that the top 32-bits of the argument - *   registers on taking a 32-bit syscall are zero - * - if you can, you should call sys_keyctl directly + * Instantiate a key with the specified compatibility multipart payload and + * link the key into the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority).  No other permissions are required. + * + * If successful, 0 will be returned. + */ +static long compat_keyctl_instantiate_key_iov( +	key_serial_t id, +	const struct compat_iovec __user *_payload_iov, +	unsigned ioc, +	key_serial_t ringid) +{ +	struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; +	long ret; + +	if (!_payload_iov || !ioc) +		goto no_payload; + +	ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc, +					   ARRAY_SIZE(iovstack), +					   iovstack, &iov); +	if (ret < 0) +		goto err; +	if (ret == 0) +		goto no_payload_free; + +	ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid); +err: +	if (iov != iovstack) +		kfree(iov); +	return ret; + +no_payload_free: +	if (iov != iovstack) +		kfree(iov); +no_payload: +	return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid); +} + +/* + * The key control system call, 32-bit compatibility version for 64-bit archs + * + * This should only be called if the 64-bit arch uses weird pointers in 32-bit + * mode or doesn't guarantee that the top 32-bits of the argument registers on + * taking a 32-bit syscall are zero.  If you can, you should call sys_keyctl() + * directly.   */ -asmlinkage long compat_sys_keyctl(u32 option, -				  u32 arg2, u32 arg3, u32 arg4, u32 arg5) +COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, +		       u32, arg2, u32, arg3, u32, arg4, u32, arg5)  {  	switch (option) {  	case KEYCTL_GET_KEYRING_ID: @@ -85,8 +128,20 @@ asmlinkage long compat_sys_keyctl(u32 option,  	case KEYCTL_SESSION_TO_PARENT:  		return keyctl_session_to_parent(); +	case KEYCTL_REJECT: +		return keyctl_reject_key(arg2, arg3, arg4, arg5); + +	case KEYCTL_INSTANTIATE_IOV: +		return compat_keyctl_instantiate_key_iov( +			arg2, compat_ptr(arg3), arg4, arg5); + +	case KEYCTL_INVALIDATE: +		return keyctl_invalidate_key(arg2); + +	case KEYCTL_GET_PERSISTENT: +		return keyctl_get_persistent(arg2, arg3); +  	default:  		return -EOPNOTSUPP;  	} - -} /* end compat_sys_keyctl() */ +} diff --git a/security/keys/encrypted-keys/Makefile b/security/keys/encrypted-keys/Makefile new file mode 100644 index 00000000000..d6f8433250a --- /dev/null +++ b/security/keys/encrypted-keys/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for encrypted keys +# + +obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys.o + +encrypted-keys-y := encrypted.o ecryptfs_format.o +masterkey-$(CONFIG_TRUSTED_KEYS) := masterkey_trusted.o +masterkey-$(CONFIG_TRUSTED_KEYS)-$(CONFIG_ENCRYPTED_KEYS) := masterkey_trusted.o +encrypted-keys-y += $(masterkey-y) $(masterkey-m-m) diff --git a/security/keys/encrypted-keys/ecryptfs_format.c b/security/keys/encrypted-keys/ecryptfs_format.c new file mode 100644 index 00000000000..6daa3b6ff9e --- /dev/null +++ b/security/keys/encrypted-keys/ecryptfs_format.c @@ -0,0 +1,81 @@ +/* + * ecryptfs_format.c: helper functions for the encrypted key type + * + * Copyright (C) 2006 International Business Machines Corp. + * Copyright (C) 2010 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Authors: + * Michael A. Halcrow <mahalcro@us.ibm.com> + * Tyler Hicks <tyhicks@ou.edu> + * Roberto Sassu <roberto.sassu@polito.it> + * + * 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, version 2 of the License. + */ + +#include <linux/module.h> +#include "ecryptfs_format.h" + +u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok) +{ +	return auth_tok->token.password.session_key_encryption_key; +} +EXPORT_SYMBOL(ecryptfs_get_auth_tok_key); + +/* + * ecryptfs_get_versions() + * + * Source code taken from the software 'ecryptfs-utils' version 83. + * + */ +void ecryptfs_get_versions(int *major, int *minor, int *file_version) +{ +	*major = ECRYPTFS_VERSION_MAJOR; +	*minor = ECRYPTFS_VERSION_MINOR; +	if (file_version) +		*file_version = ECRYPTFS_SUPPORTED_FILE_VERSION; +} +EXPORT_SYMBOL(ecryptfs_get_versions); + +/* + * ecryptfs_fill_auth_tok - fill the ecryptfs_auth_tok structure + * + * Fill the ecryptfs_auth_tok structure with required ecryptfs data. + * The source code is inspired to the original function generate_payload() + * shipped with the software 'ecryptfs-utils' version 83. + * + */ +int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok, +			   const char *key_desc) +{ +	int major, minor; + +	ecryptfs_get_versions(&major, &minor, NULL); +	auth_tok->version = (((uint16_t)(major << 8) & 0xFF00) +			     | ((uint16_t)minor & 0x00FF)); +	auth_tok->token_type = ECRYPTFS_PASSWORD; +	strncpy((char *)auth_tok->token.password.signature, key_desc, +		ECRYPTFS_PASSWORD_SIG_SIZE); +	auth_tok->token.password.session_key_encryption_key_bytes = +		ECRYPTFS_MAX_KEY_BYTES; +	/* +	 * Removed auth_tok->token.password.salt and +	 * auth_tok->token.password.session_key_encryption_key +	 * initialization from the original code +	 */ +	/* TODO: Make the hash parameterizable via policy */ +	auth_tok->token.password.flags |= +		ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET; +	/* The kernel code will encrypt the session key. */ +	auth_tok->session_key.encrypted_key[0] = 0; +	auth_tok->session_key.encrypted_key_size = 0; +	/* Default; subject to change by kernel eCryptfs */ +	auth_tok->token.password.hash_algo = PGP_DIGEST_ALGO_SHA512; +	auth_tok->token.password.flags &= ~(ECRYPTFS_PERSISTENT_PASSWORD); +	return 0; +} +EXPORT_SYMBOL(ecryptfs_fill_auth_tok); + +MODULE_LICENSE("GPL"); diff --git a/security/keys/encrypted-keys/ecryptfs_format.h b/security/keys/encrypted-keys/ecryptfs_format.h new file mode 100644 index 00000000000..40294de238b --- /dev/null +++ b/security/keys/encrypted-keys/ecryptfs_format.h @@ -0,0 +1,30 @@ +/* + * ecryptfs_format.h: helper functions for the encrypted key type + * + * Copyright (C) 2006 International Business Machines Corp. + * Copyright (C) 2010 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Authors: + * Michael A. Halcrow <mahalcro@us.ibm.com> + * Tyler Hicks <tyhicks@ou.edu> + * Roberto Sassu <roberto.sassu@polito.it> + * + * 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, version 2 of the License. + */ + +#ifndef __KEYS_ECRYPTFS_H +#define __KEYS_ECRYPTFS_H + +#include <linux/ecryptfs.h> + +#define PGP_DIGEST_ALGO_SHA512   10 + +u8 *ecryptfs_get_auth_tok_key(struct ecryptfs_auth_tok *auth_tok); +void ecryptfs_get_versions(int *major, int *minor, int *file_version); +int ecryptfs_fill_auth_tok(struct ecryptfs_auth_tok *auth_tok, +			   const char *key_desc); + +#endif /* __KEYS_ECRYPTFS_H */ diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c new file mode 100644 index 00000000000..5fe443d120a --- /dev/null +++ b/security/keys/encrypted-keys/encrypted.c @@ -0,0 +1,1040 @@ +/* + * Copyright (C) 2010 IBM Corporation + * Copyright (C) 2010 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Authors: + * Mimi Zohar <zohar@us.ibm.com> + * Roberto Sassu <roberto.sassu@polito.it> + * + * 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, version 2 of the License. + * + * See Documentation/security/keys-trusted-encrypted.txt + */ + +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/parser.h> +#include <linux/string.h> +#include <linux/err.h> +#include <keys/user-type.h> +#include <keys/trusted-type.h> +#include <keys/encrypted-type.h> +#include <linux/key-type.h> +#include <linux/random.h> +#include <linux/rcupdate.h> +#include <linux/scatterlist.h> +#include <linux/crypto.h> +#include <linux/ctype.h> +#include <crypto/hash.h> +#include <crypto/sha.h> +#include <crypto/aes.h> + +#include "encrypted.h" +#include "ecryptfs_format.h" + +static const char KEY_TRUSTED_PREFIX[] = "trusted:"; +static const char KEY_USER_PREFIX[] = "user:"; +static const char hash_alg[] = "sha256"; +static const char hmac_alg[] = "hmac(sha256)"; +static const char blkcipher_alg[] = "cbc(aes)"; +static const char key_format_default[] = "default"; +static const char key_format_ecryptfs[] = "ecryptfs"; +static unsigned int ivsize; +static int blksize; + +#define KEY_TRUSTED_PREFIX_LEN (sizeof (KEY_TRUSTED_PREFIX) - 1) +#define KEY_USER_PREFIX_LEN (sizeof (KEY_USER_PREFIX) - 1) +#define KEY_ECRYPTFS_DESC_LEN 16 +#define HASH_SIZE SHA256_DIGEST_SIZE +#define MAX_DATA_SIZE 4096 +#define MIN_DATA_SIZE  20 + +struct sdesc { +	struct shash_desc shash; +	char ctx[]; +}; + +static struct crypto_shash *hashalg; +static struct crypto_shash *hmacalg; + +enum { +	Opt_err = -1, Opt_new, Opt_load, Opt_update +}; + +enum { +	Opt_error = -1, Opt_default, Opt_ecryptfs +}; + +static const match_table_t key_format_tokens = { +	{Opt_default, "default"}, +	{Opt_ecryptfs, "ecryptfs"}, +	{Opt_error, NULL} +}; + +static const match_table_t key_tokens = { +	{Opt_new, "new"}, +	{Opt_load, "load"}, +	{Opt_update, "update"}, +	{Opt_err, NULL} +}; + +static int aes_get_sizes(void) +{ +	struct crypto_blkcipher *tfm; + +	tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(tfm)) { +		pr_err("encrypted_key: failed to alloc_cipher (%ld)\n", +		       PTR_ERR(tfm)); +		return PTR_ERR(tfm); +	} +	ivsize = crypto_blkcipher_ivsize(tfm); +	blksize = crypto_blkcipher_blocksize(tfm); +	crypto_free_blkcipher(tfm); +	return 0; +} + +/* + * valid_ecryptfs_desc - verify the description of a new/loaded encrypted key + * + * The description of a encrypted key with format 'ecryptfs' must contain + * exactly 16 hexadecimal characters. + * + */ +static int valid_ecryptfs_desc(const char *ecryptfs_desc) +{ +	int i; + +	if (strlen(ecryptfs_desc) != KEY_ECRYPTFS_DESC_LEN) { +		pr_err("encrypted_key: key description must be %d hexadecimal " +		       "characters long\n", KEY_ECRYPTFS_DESC_LEN); +		return -EINVAL; +	} + +	for (i = 0; i < KEY_ECRYPTFS_DESC_LEN; i++) { +		if (!isxdigit(ecryptfs_desc[i])) { +			pr_err("encrypted_key: key description must contain " +			       "only hexadecimal characters\n"); +			return -EINVAL; +		} +	} + +	return 0; +} + +/* + * valid_master_desc - verify the 'key-type:desc' of a new/updated master-key + * + * key-type:= "trusted:" | "user:" + * desc:= master-key description + * + * Verify that 'key-type' is valid and that 'desc' exists. On key update, + * only the master key description is permitted to change, not the key-type. + * The key-type remains constant. + * + * On success returns 0, otherwise -EINVAL. + */ +static int valid_master_desc(const char *new_desc, const char *orig_desc) +{ +	if (!memcmp(new_desc, KEY_TRUSTED_PREFIX, KEY_TRUSTED_PREFIX_LEN)) { +		if (strlen(new_desc) == KEY_TRUSTED_PREFIX_LEN) +			goto out; +		if (orig_desc) +			if (memcmp(new_desc, orig_desc, KEY_TRUSTED_PREFIX_LEN)) +				goto out; +	} else if (!memcmp(new_desc, KEY_USER_PREFIX, KEY_USER_PREFIX_LEN)) { +		if (strlen(new_desc) == KEY_USER_PREFIX_LEN) +			goto out; +		if (orig_desc) +			if (memcmp(new_desc, orig_desc, KEY_USER_PREFIX_LEN)) +				goto out; +	} else +		goto out; +	return 0; +out: +	return -EINVAL; +} + +/* + * datablob_parse - parse the keyctl data + * + * datablob format: + * new [<format>] <master-key name> <decrypted data length> + * load [<format>] <master-key name> <decrypted data length> + *     <encrypted iv + data> + * update <new-master-key name> + * + * Tokenizes a copy of the keyctl data, returning a pointer to each token, + * which is null terminated. + * + * On success returns 0, otherwise -EINVAL. + */ +static int datablob_parse(char *datablob, const char **format, +			  char **master_desc, char **decrypted_datalen, +			  char **hex_encoded_iv) +{ +	substring_t args[MAX_OPT_ARGS]; +	int ret = -EINVAL; +	int key_cmd; +	int key_format; +	char *p, *keyword; + +	keyword = strsep(&datablob, " \t"); +	if (!keyword) { +		pr_info("encrypted_key: insufficient parameters specified\n"); +		return ret; +	} +	key_cmd = match_token(keyword, key_tokens, args); + +	/* Get optional format: default | ecryptfs */ +	p = strsep(&datablob, " \t"); +	if (!p) { +		pr_err("encrypted_key: insufficient parameters specified\n"); +		return ret; +	} + +	key_format = match_token(p, key_format_tokens, args); +	switch (key_format) { +	case Opt_ecryptfs: +	case Opt_default: +		*format = p; +		*master_desc = strsep(&datablob, " \t"); +		break; +	case Opt_error: +		*master_desc = p; +		break; +	} + +	if (!*master_desc) { +		pr_info("encrypted_key: master key parameter is missing\n"); +		goto out; +	} + +	if (valid_master_desc(*master_desc, NULL) < 0) { +		pr_info("encrypted_key: master key parameter \'%s\' " +			"is invalid\n", *master_desc); +		goto out; +	} + +	if (decrypted_datalen) { +		*decrypted_datalen = strsep(&datablob, " \t"); +		if (!*decrypted_datalen) { +			pr_info("encrypted_key: keylen parameter is missing\n"); +			goto out; +		} +	} + +	switch (key_cmd) { +	case Opt_new: +		if (!decrypted_datalen) { +			pr_info("encrypted_key: keyword \'%s\' not allowed " +				"when called from .update method\n", keyword); +			break; +		} +		ret = 0; +		break; +	case Opt_load: +		if (!decrypted_datalen) { +			pr_info("encrypted_key: keyword \'%s\' not allowed " +				"when called from .update method\n", keyword); +			break; +		} +		*hex_encoded_iv = strsep(&datablob, " \t"); +		if (!*hex_encoded_iv) { +			pr_info("encrypted_key: hex blob is missing\n"); +			break; +		} +		ret = 0; +		break; +	case Opt_update: +		if (decrypted_datalen) { +			pr_info("encrypted_key: keyword \'%s\' not allowed " +				"when called from .instantiate method\n", +				keyword); +			break; +		} +		ret = 0; +		break; +	case Opt_err: +		pr_info("encrypted_key: keyword \'%s\' not recognized\n", +			keyword); +		break; +	} +out: +	return ret; +} + +/* + * datablob_format - format as an ascii string, before copying to userspace + */ +static char *datablob_format(struct encrypted_key_payload *epayload, +			     size_t asciiblob_len) +{ +	char *ascii_buf, *bufp; +	u8 *iv = epayload->iv; +	int len; +	int i; + +	ascii_buf = kmalloc(asciiblob_len + 1, GFP_KERNEL); +	if (!ascii_buf) +		goto out; + +	ascii_buf[asciiblob_len] = '\0'; + +	/* copy datablob master_desc and datalen strings */ +	len = sprintf(ascii_buf, "%s %s %s ", epayload->format, +		      epayload->master_desc, epayload->datalen); + +	/* convert the hex encoded iv, encrypted-data and HMAC to ascii */ +	bufp = &ascii_buf[len]; +	for (i = 0; i < (asciiblob_len - len) / 2; i++) +		bufp = hex_byte_pack(bufp, iv[i]); +out: +	return ascii_buf; +} + +/* + * request_user_key - request the user key + * + * Use a user provided key to encrypt/decrypt an encrypted-key. + */ +static struct key *request_user_key(const char *master_desc, u8 **master_key, +				    size_t *master_keylen) +{ +	struct user_key_payload *upayload; +	struct key *ukey; + +	ukey = request_key(&key_type_user, master_desc, NULL); +	if (IS_ERR(ukey)) +		goto error; + +	down_read(&ukey->sem); +	upayload = ukey->payload.data; +	*master_key = upayload->data; +	*master_keylen = upayload->datalen; +error: +	return ukey; +} + +static struct sdesc *alloc_sdesc(struct crypto_shash *alg) +{ +	struct sdesc *sdesc; +	int size; + +	size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); +	sdesc = kmalloc(size, GFP_KERNEL); +	if (!sdesc) +		return ERR_PTR(-ENOMEM); +	sdesc->shash.tfm = alg; +	sdesc->shash.flags = 0x0; +	return sdesc; +} + +static int calc_hmac(u8 *digest, const u8 *key, unsigned int keylen, +		     const u8 *buf, unsigned int buflen) +{ +	struct sdesc *sdesc; +	int ret; + +	sdesc = alloc_sdesc(hmacalg); +	if (IS_ERR(sdesc)) { +		pr_info("encrypted_key: can't alloc %s\n", hmac_alg); +		return PTR_ERR(sdesc); +	} + +	ret = crypto_shash_setkey(hmacalg, key, keylen); +	if (!ret) +		ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest); +	kfree(sdesc); +	return ret; +} + +static int calc_hash(u8 *digest, const u8 *buf, unsigned int buflen) +{ +	struct sdesc *sdesc; +	int ret; + +	sdesc = alloc_sdesc(hashalg); +	if (IS_ERR(sdesc)) { +		pr_info("encrypted_key: can't alloc %s\n", hash_alg); +		return PTR_ERR(sdesc); +	} + +	ret = crypto_shash_digest(&sdesc->shash, buf, buflen, digest); +	kfree(sdesc); +	return ret; +} + +enum derived_key_type { ENC_KEY, AUTH_KEY }; + +/* Derive authentication/encryption key from trusted key */ +static int get_derived_key(u8 *derived_key, enum derived_key_type key_type, +			   const u8 *master_key, size_t master_keylen) +{ +	u8 *derived_buf; +	unsigned int derived_buf_len; +	int ret; + +	derived_buf_len = strlen("AUTH_KEY") + 1 + master_keylen; +	if (derived_buf_len < HASH_SIZE) +		derived_buf_len = HASH_SIZE; + +	derived_buf = kzalloc(derived_buf_len, GFP_KERNEL); +	if (!derived_buf) { +		pr_err("encrypted_key: out of memory\n"); +		return -ENOMEM; +	} +	if (key_type) +		strcpy(derived_buf, "AUTH_KEY"); +	else +		strcpy(derived_buf, "ENC_KEY"); + +	memcpy(derived_buf + strlen(derived_buf) + 1, master_key, +	       master_keylen); +	ret = calc_hash(derived_key, derived_buf, derived_buf_len); +	kfree(derived_buf); +	return ret; +} + +static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key, +			       unsigned int key_len, const u8 *iv, +			       unsigned int ivsize) +{ +	int ret; + +	desc->tfm = crypto_alloc_blkcipher(blkcipher_alg, 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(desc->tfm)) { +		pr_err("encrypted_key: failed to load %s transform (%ld)\n", +		       blkcipher_alg, PTR_ERR(desc->tfm)); +		return PTR_ERR(desc->tfm); +	} +	desc->flags = 0; + +	ret = crypto_blkcipher_setkey(desc->tfm, key, key_len); +	if (ret < 0) { +		pr_err("encrypted_key: failed to setkey (%d)\n", ret); +		crypto_free_blkcipher(desc->tfm); +		return ret; +	} +	crypto_blkcipher_set_iv(desc->tfm, iv, ivsize); +	return 0; +} + +static struct key *request_master_key(struct encrypted_key_payload *epayload, +				      u8 **master_key, size_t *master_keylen) +{ +	struct key *mkey = NULL; + +	if (!strncmp(epayload->master_desc, KEY_TRUSTED_PREFIX, +		     KEY_TRUSTED_PREFIX_LEN)) { +		mkey = request_trusted_key(epayload->master_desc + +					   KEY_TRUSTED_PREFIX_LEN, +					   master_key, master_keylen); +	} else if (!strncmp(epayload->master_desc, KEY_USER_PREFIX, +			    KEY_USER_PREFIX_LEN)) { +		mkey = request_user_key(epayload->master_desc + +					KEY_USER_PREFIX_LEN, +					master_key, master_keylen); +	} else +		goto out; + +	if (IS_ERR(mkey)) { +		int ret = PTR_ERR(mkey); + +		if (ret == -ENOTSUPP) +			pr_info("encrypted_key: key %s not supported", +				epayload->master_desc); +		else +			pr_info("encrypted_key: key %s not found", +				epayload->master_desc); +		goto out; +	} + +	dump_master_key(*master_key, *master_keylen); +out: +	return mkey; +} + +/* Before returning data to userspace, encrypt decrypted data. */ +static int derived_key_encrypt(struct encrypted_key_payload *epayload, +			       const u8 *derived_key, +			       unsigned int derived_keylen) +{ +	struct scatterlist sg_in[2]; +	struct scatterlist sg_out[1]; +	struct blkcipher_desc desc; +	unsigned int encrypted_datalen; +	unsigned int padlen; +	char pad[16]; +	int ret; + +	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); +	padlen = encrypted_datalen - epayload->decrypted_datalen; + +	ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, +				  epayload->iv, ivsize); +	if (ret < 0) +		goto out; +	dump_decrypted_data(epayload); + +	memset(pad, 0, sizeof pad); +	sg_init_table(sg_in, 2); +	sg_set_buf(&sg_in[0], epayload->decrypted_data, +		   epayload->decrypted_datalen); +	sg_set_buf(&sg_in[1], pad, padlen); + +	sg_init_table(sg_out, 1); +	sg_set_buf(sg_out, epayload->encrypted_data, encrypted_datalen); + +	ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in, encrypted_datalen); +	crypto_free_blkcipher(desc.tfm); +	if (ret < 0) +		pr_err("encrypted_key: failed to encrypt (%d)\n", ret); +	else +		dump_encrypted_data(epayload, encrypted_datalen); +out: +	return ret; +} + +static int datablob_hmac_append(struct encrypted_key_payload *epayload, +				const u8 *master_key, size_t master_keylen) +{ +	u8 derived_key[HASH_SIZE]; +	u8 *digest; +	int ret; + +	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen); +	if (ret < 0) +		goto out; + +	digest = epayload->format + epayload->datablob_len; +	ret = calc_hmac(digest, derived_key, sizeof derived_key, +			epayload->format, epayload->datablob_len); +	if (!ret) +		dump_hmac(NULL, digest, HASH_SIZE); +out: +	return ret; +} + +/* verify HMAC before decrypting encrypted key */ +static int datablob_hmac_verify(struct encrypted_key_payload *epayload, +				const u8 *format, const u8 *master_key, +				size_t master_keylen) +{ +	u8 derived_key[HASH_SIZE]; +	u8 digest[HASH_SIZE]; +	int ret; +	char *p; +	unsigned short len; + +	ret = get_derived_key(derived_key, AUTH_KEY, master_key, master_keylen); +	if (ret < 0) +		goto out; + +	len = epayload->datablob_len; +	if (!format) { +		p = epayload->master_desc; +		len -= strlen(epayload->format) + 1; +	} else +		p = epayload->format; + +	ret = calc_hmac(digest, derived_key, sizeof derived_key, p, len); +	if (ret < 0) +		goto out; +	ret = memcmp(digest, epayload->format + epayload->datablob_len, +		     sizeof digest); +	if (ret) { +		ret = -EINVAL; +		dump_hmac("datablob", +			  epayload->format + epayload->datablob_len, +			  HASH_SIZE); +		dump_hmac("calc", digest, HASH_SIZE); +	} +out: +	return ret; +} + +static int derived_key_decrypt(struct encrypted_key_payload *epayload, +			       const u8 *derived_key, +			       unsigned int derived_keylen) +{ +	struct scatterlist sg_in[1]; +	struct scatterlist sg_out[2]; +	struct blkcipher_desc desc; +	unsigned int encrypted_datalen; +	char pad[16]; +	int ret; + +	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); +	ret = init_blkcipher_desc(&desc, derived_key, derived_keylen, +				  epayload->iv, ivsize); +	if (ret < 0) +		goto out; +	dump_encrypted_data(epayload, encrypted_datalen); + +	memset(pad, 0, sizeof pad); +	sg_init_table(sg_in, 1); +	sg_init_table(sg_out, 2); +	sg_set_buf(sg_in, epayload->encrypted_data, encrypted_datalen); +	sg_set_buf(&sg_out[0], epayload->decrypted_data, +		   epayload->decrypted_datalen); +	sg_set_buf(&sg_out[1], pad, sizeof pad); + +	ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, encrypted_datalen); +	crypto_free_blkcipher(desc.tfm); +	if (ret < 0) +		goto out; +	dump_decrypted_data(epayload); +out: +	return ret; +} + +/* Allocate memory for decrypted key and datablob. */ +static struct encrypted_key_payload *encrypted_key_alloc(struct key *key, +							 const char *format, +							 const char *master_desc, +							 const char *datalen) +{ +	struct encrypted_key_payload *epayload = NULL; +	unsigned short datablob_len; +	unsigned short decrypted_datalen; +	unsigned short payload_datalen; +	unsigned int encrypted_datalen; +	unsigned int format_len; +	long dlen; +	int ret; + +	ret = kstrtol(datalen, 10, &dlen); +	if (ret < 0 || dlen < MIN_DATA_SIZE || dlen > MAX_DATA_SIZE) +		return ERR_PTR(-EINVAL); + +	format_len = (!format) ? strlen(key_format_default) : strlen(format); +	decrypted_datalen = dlen; +	payload_datalen = decrypted_datalen; +	if (format && !strcmp(format, key_format_ecryptfs)) { +		if (dlen != ECRYPTFS_MAX_KEY_BYTES) { +			pr_err("encrypted_key: keylen for the ecryptfs format " +			       "must be equal to %d bytes\n", +			       ECRYPTFS_MAX_KEY_BYTES); +			return ERR_PTR(-EINVAL); +		} +		decrypted_datalen = ECRYPTFS_MAX_KEY_BYTES; +		payload_datalen = sizeof(struct ecryptfs_auth_tok); +	} + +	encrypted_datalen = roundup(decrypted_datalen, blksize); + +	datablob_len = format_len + 1 + strlen(master_desc) + 1 +	    + strlen(datalen) + 1 + ivsize + 1 + encrypted_datalen; + +	ret = key_payload_reserve(key, payload_datalen + datablob_len +				  + HASH_SIZE + 1); +	if (ret < 0) +		return ERR_PTR(ret); + +	epayload = kzalloc(sizeof(*epayload) + payload_datalen + +			   datablob_len + HASH_SIZE + 1, GFP_KERNEL); +	if (!epayload) +		return ERR_PTR(-ENOMEM); + +	epayload->payload_datalen = payload_datalen; +	epayload->decrypted_datalen = decrypted_datalen; +	epayload->datablob_len = datablob_len; +	return epayload; +} + +static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, +				 const char *format, const char *hex_encoded_iv) +{ +	struct key *mkey; +	u8 derived_key[HASH_SIZE]; +	u8 *master_key; +	u8 *hmac; +	const char *hex_encoded_data; +	unsigned int encrypted_datalen; +	size_t master_keylen; +	size_t asciilen; +	int ret; + +	encrypted_datalen = roundup(epayload->decrypted_datalen, blksize); +	asciilen = (ivsize + 1 + encrypted_datalen + HASH_SIZE) * 2; +	if (strlen(hex_encoded_iv) != asciilen) +		return -EINVAL; + +	hex_encoded_data = hex_encoded_iv + (2 * ivsize) + 2; +	ret = hex2bin(epayload->iv, hex_encoded_iv, ivsize); +	if (ret < 0) +		return -EINVAL; +	ret = hex2bin(epayload->encrypted_data, hex_encoded_data, +		      encrypted_datalen); +	if (ret < 0) +		return -EINVAL; + +	hmac = epayload->format + epayload->datablob_len; +	ret = hex2bin(hmac, hex_encoded_data + (encrypted_datalen * 2), +		      HASH_SIZE); +	if (ret < 0) +		return -EINVAL; + +	mkey = request_master_key(epayload, &master_key, &master_keylen); +	if (IS_ERR(mkey)) +		return PTR_ERR(mkey); + +	ret = datablob_hmac_verify(epayload, format, master_key, master_keylen); +	if (ret < 0) { +		pr_err("encrypted_key: bad hmac (%d)\n", ret); +		goto out; +	} + +	ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen); +	if (ret < 0) +		goto out; + +	ret = derived_key_decrypt(epayload, derived_key, sizeof derived_key); +	if (ret < 0) +		pr_err("encrypted_key: failed to decrypt key (%d)\n", ret); +out: +	up_read(&mkey->sem); +	key_put(mkey); +	return ret; +} + +static void __ekey_init(struct encrypted_key_payload *epayload, +			const char *format, const char *master_desc, +			const char *datalen) +{ +	unsigned int format_len; + +	format_len = (!format) ? strlen(key_format_default) : strlen(format); +	epayload->format = epayload->payload_data + epayload->payload_datalen; +	epayload->master_desc = epayload->format + format_len + 1; +	epayload->datalen = epayload->master_desc + strlen(master_desc) + 1; +	epayload->iv = epayload->datalen + strlen(datalen) + 1; +	epayload->encrypted_data = epayload->iv + ivsize + 1; +	epayload->decrypted_data = epayload->payload_data; + +	if (!format) +		memcpy(epayload->format, key_format_default, format_len); +	else { +		if (!strcmp(format, key_format_ecryptfs)) +			epayload->decrypted_data = +				ecryptfs_get_auth_tok_key((struct ecryptfs_auth_tok *)epayload->payload_data); + +		memcpy(epayload->format, format, format_len); +	} + +	memcpy(epayload->master_desc, master_desc, strlen(master_desc)); +	memcpy(epayload->datalen, datalen, strlen(datalen)); +} + +/* + * encrypted_init - initialize an encrypted key + * + * For a new key, use a random number for both the iv and data + * itself.  For an old key, decrypt the hex encoded data. + */ +static int encrypted_init(struct encrypted_key_payload *epayload, +			  const char *key_desc, const char *format, +			  const char *master_desc, const char *datalen, +			  const char *hex_encoded_iv) +{ +	int ret = 0; + +	if (format && !strcmp(format, key_format_ecryptfs)) { +		ret = valid_ecryptfs_desc(key_desc); +		if (ret < 0) +			return ret; + +		ecryptfs_fill_auth_tok((struct ecryptfs_auth_tok *)epayload->payload_data, +				       key_desc); +	} + +	__ekey_init(epayload, format, master_desc, datalen); +	if (!hex_encoded_iv) { +		get_random_bytes(epayload->iv, ivsize); + +		get_random_bytes(epayload->decrypted_data, +				 epayload->decrypted_datalen); +	} else +		ret = encrypted_key_decrypt(epayload, format, hex_encoded_iv); +	return ret; +} + +/* + * encrypted_instantiate - instantiate an encrypted key + * + * Decrypt an existing encrypted datablob or create a new encrypted key + * based on a kernel random number. + * + * On success, return 0. Otherwise return errno. + */ +static int encrypted_instantiate(struct key *key, +				 struct key_preparsed_payload *prep) +{ +	struct encrypted_key_payload *epayload = NULL; +	char *datablob = NULL; +	const char *format = NULL; +	char *master_desc = NULL; +	char *decrypted_datalen = NULL; +	char *hex_encoded_iv = NULL; +	size_t datalen = prep->datalen; +	int ret; + +	if (datalen <= 0 || datalen > 32767 || !prep->data) +		return -EINVAL; + +	datablob = kmalloc(datalen + 1, GFP_KERNEL); +	if (!datablob) +		return -ENOMEM; +	datablob[datalen] = 0; +	memcpy(datablob, prep->data, datalen); +	ret = datablob_parse(datablob, &format, &master_desc, +			     &decrypted_datalen, &hex_encoded_iv); +	if (ret < 0) +		goto out; + +	epayload = encrypted_key_alloc(key, format, master_desc, +				       decrypted_datalen); +	if (IS_ERR(epayload)) { +		ret = PTR_ERR(epayload); +		goto out; +	} +	ret = encrypted_init(epayload, key->description, format, master_desc, +			     decrypted_datalen, hex_encoded_iv); +	if (ret < 0) { +		kfree(epayload); +		goto out; +	} + +	rcu_assign_keypointer(key, epayload); +out: +	kfree(datablob); +	return ret; +} + +static void encrypted_rcu_free(struct rcu_head *rcu) +{ +	struct encrypted_key_payload *epayload; + +	epayload = container_of(rcu, struct encrypted_key_payload, rcu); +	memset(epayload->decrypted_data, 0, epayload->decrypted_datalen); +	kfree(epayload); +} + +/* + * encrypted_update - update the master key description + * + * Change the master key description for an existing encrypted key. + * The next read will return an encrypted datablob using the new + * master key description. + * + * On success, return 0. Otherwise return errno. + */ +static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) +{ +	struct encrypted_key_payload *epayload = key->payload.data; +	struct encrypted_key_payload *new_epayload; +	char *buf; +	char *new_master_desc = NULL; +	const char *format = NULL; +	size_t datalen = prep->datalen; +	int ret = 0; + +	if (datalen <= 0 || datalen > 32767 || !prep->data) +		return -EINVAL; + +	buf = kmalloc(datalen + 1, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	buf[datalen] = 0; +	memcpy(buf, prep->data, datalen); +	ret = datablob_parse(buf, &format, &new_master_desc, NULL, NULL); +	if (ret < 0) +		goto out; + +	ret = valid_master_desc(new_master_desc, epayload->master_desc); +	if (ret < 0) +		goto out; + +	new_epayload = encrypted_key_alloc(key, epayload->format, +					   new_master_desc, epayload->datalen); +	if (IS_ERR(new_epayload)) { +		ret = PTR_ERR(new_epayload); +		goto out; +	} + +	__ekey_init(new_epayload, epayload->format, new_master_desc, +		    epayload->datalen); + +	memcpy(new_epayload->iv, epayload->iv, ivsize); +	memcpy(new_epayload->payload_data, epayload->payload_data, +	       epayload->payload_datalen); + +	rcu_assign_keypointer(key, new_epayload); +	call_rcu(&epayload->rcu, encrypted_rcu_free); +out: +	kfree(buf); +	return ret; +} + +/* + * encrypted_read - format and copy the encrypted data to userspace + * + * The resulting datablob format is: + * <master-key name> <decrypted data length> <encrypted iv> <encrypted data> + * + * On success, return to userspace the encrypted key datablob size. + */ +static long encrypted_read(const struct key *key, char __user *buffer, +			   size_t buflen) +{ +	struct encrypted_key_payload *epayload; +	struct key *mkey; +	u8 *master_key; +	size_t master_keylen; +	char derived_key[HASH_SIZE]; +	char *ascii_buf; +	size_t asciiblob_len; +	int ret; + +	epayload = rcu_dereference_key(key); + +	/* returns the hex encoded iv, encrypted-data, and hmac as ascii */ +	asciiblob_len = epayload->datablob_len + ivsize + 1 +	    + roundup(epayload->decrypted_datalen, blksize) +	    + (HASH_SIZE * 2); + +	if (!buffer || buflen < asciiblob_len) +		return asciiblob_len; + +	mkey = request_master_key(epayload, &master_key, &master_keylen); +	if (IS_ERR(mkey)) +		return PTR_ERR(mkey); + +	ret = get_derived_key(derived_key, ENC_KEY, master_key, master_keylen); +	if (ret < 0) +		goto out; + +	ret = derived_key_encrypt(epayload, derived_key, sizeof derived_key); +	if (ret < 0) +		goto out; + +	ret = datablob_hmac_append(epayload, master_key, master_keylen); +	if (ret < 0) +		goto out; + +	ascii_buf = datablob_format(epayload, asciiblob_len); +	if (!ascii_buf) { +		ret = -ENOMEM; +		goto out; +	} + +	up_read(&mkey->sem); +	key_put(mkey); + +	if (copy_to_user(buffer, ascii_buf, asciiblob_len) != 0) +		ret = -EFAULT; +	kfree(ascii_buf); + +	return asciiblob_len; +out: +	up_read(&mkey->sem); +	key_put(mkey); +	return ret; +} + +/* + * encrypted_destroy - before freeing the key, clear the decrypted data + * + * Before freeing the key, clear the memory containing the decrypted + * key data. + */ +static void encrypted_destroy(struct key *key) +{ +	struct encrypted_key_payload *epayload = key->payload.data; + +	if (!epayload) +		return; + +	memset(epayload->decrypted_data, 0, epayload->decrypted_datalen); +	kfree(key->payload.data); +} + +struct key_type key_type_encrypted = { +	.name = "encrypted", +	.instantiate = encrypted_instantiate, +	.update = encrypted_update, +	.match = user_match, +	.destroy = encrypted_destroy, +	.describe = user_describe, +	.read = encrypted_read, +}; +EXPORT_SYMBOL_GPL(key_type_encrypted); + +static void encrypted_shash_release(void) +{ +	if (hashalg) +		crypto_free_shash(hashalg); +	if (hmacalg) +		crypto_free_shash(hmacalg); +} + +static int __init encrypted_shash_alloc(void) +{ +	int ret; + +	hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(hmacalg)) { +		pr_info("encrypted_key: could not allocate crypto %s\n", +			hmac_alg); +		return PTR_ERR(hmacalg); +	} + +	hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(hashalg)) { +		pr_info("encrypted_key: could not allocate crypto %s\n", +			hash_alg); +		ret = PTR_ERR(hashalg); +		goto hashalg_fail; +	} + +	return 0; + +hashalg_fail: +	crypto_free_shash(hmacalg); +	return ret; +} + +static int __init init_encrypted(void) +{ +	int ret; + +	ret = encrypted_shash_alloc(); +	if (ret < 0) +		return ret; +	ret = register_key_type(&key_type_encrypted); +	if (ret < 0) +		goto out; +	return aes_get_sizes(); +out: +	encrypted_shash_release(); +	return ret; + +} + +static void __exit cleanup_encrypted(void) +{ +	encrypted_shash_release(); +	unregister_key_type(&key_type_encrypted); +} + +late_initcall(init_encrypted); +module_exit(cleanup_encrypted); + +MODULE_LICENSE("GPL"); diff --git a/security/keys/encrypted-keys/encrypted.h b/security/keys/encrypted-keys/encrypted.h new file mode 100644 index 00000000000..8136a2d44c6 --- /dev/null +++ b/security/keys/encrypted-keys/encrypted.h @@ -0,0 +1,66 @@ +#ifndef __ENCRYPTED_KEY_H +#define __ENCRYPTED_KEY_H + +#define ENCRYPTED_DEBUG 0 +#if defined(CONFIG_TRUSTED_KEYS) || \ +  (defined(CONFIG_TRUSTED_KEYS_MODULE) && defined(CONFIG_ENCRYPTED_KEYS_MODULE)) +extern struct key *request_trusted_key(const char *trusted_desc, +				       u8 **master_key, size_t *master_keylen); +#else +static inline struct key *request_trusted_key(const char *trusted_desc, +					      u8 **master_key, +					      size_t *master_keylen) +{ +	return ERR_PTR(-EOPNOTSUPP); +} +#endif + +#if ENCRYPTED_DEBUG +static inline void dump_master_key(const u8 *master_key, size_t master_keylen) +{ +	print_hex_dump(KERN_ERR, "master key: ", DUMP_PREFIX_NONE, 32, 1, +		       master_key, master_keylen, 0); +} + +static inline void dump_decrypted_data(struct encrypted_key_payload *epayload) +{ +	print_hex_dump(KERN_ERR, "decrypted data: ", DUMP_PREFIX_NONE, 32, 1, +		       epayload->decrypted_data, +		       epayload->decrypted_datalen, 0); +} + +static inline void dump_encrypted_data(struct encrypted_key_payload *epayload, +				       unsigned int encrypted_datalen) +{ +	print_hex_dump(KERN_ERR, "encrypted data: ", DUMP_PREFIX_NONE, 32, 1, +		       epayload->encrypted_data, encrypted_datalen, 0); +} + +static inline void dump_hmac(const char *str, const u8 *digest, +			     unsigned int hmac_size) +{ +	if (str) +		pr_info("encrypted_key: %s", str); +	print_hex_dump(KERN_ERR, "hmac: ", DUMP_PREFIX_NONE, 32, 1, digest, +		       hmac_size, 0); +} +#else +static inline void dump_master_key(const u8 *master_key, size_t master_keylen) +{ +} + +static inline void dump_decrypted_data(struct encrypted_key_payload *epayload) +{ +} + +static inline void dump_encrypted_data(struct encrypted_key_payload *epayload, +				       unsigned int encrypted_datalen) +{ +} + +static inline void dump_hmac(const char *str, const u8 *digest, +			     unsigned int hmac_size) +{ +} +#endif +#endif diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c new file mode 100644 index 00000000000..013f7e5d3a2 --- /dev/null +++ b/security/keys/encrypted-keys/masterkey_trusted.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 IBM Corporation + * Copyright (C) 2010 Politecnico di Torino, Italy + *                    TORSEC group -- http://security.polito.it + * + * Authors: + * Mimi Zohar <zohar@us.ibm.com> + * Roberto Sassu <roberto.sassu@polito.it> + * + * 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, version 2 of the License. + * + * See Documentation/security/keys-trusted-encrypted.txt + */ + +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/err.h> +#include <keys/trusted-type.h> +#include <keys/encrypted-type.h> +#include "encrypted.h" + +/* + * request_trusted_key - request the trusted key + * + * Trusted keys are sealed to PCRs and other metadata. Although userspace + * manages both trusted/encrypted key-types, like the encrypted key type + * data, trusted key type data is not visible decrypted from userspace. + */ +struct key *request_trusted_key(const char *trusted_desc, +				u8 **master_key, size_t *master_keylen) +{ +	struct trusted_key_payload *tpayload; +	struct key *tkey; + +	tkey = request_key(&key_type_trusted, trusted_desc, NULL); +	if (IS_ERR(tkey)) +		goto error; + +	down_read(&tkey->sem); +	tpayload = tkey->payload.data; +	*master_key = tpayload->key; +	*master_keylen = tpayload->key_len; +error: +	return tkey; +} diff --git a/security/keys/gc.c b/security/keys/gc.c index a46e825cbf0..d3222b6d7d5 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -1,6 +1,6 @@  /* Key garbage collector   * - * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com)   *   * This program is free software; you can redistribute it and/or @@ -10,6 +10,8 @@   */  #include <linux/module.h> +#include <linux/slab.h> +#include <linux/security.h>  #include <keys/keyring-type.h>  #include "internal.h" @@ -19,21 +21,37 @@  unsigned key_gc_delay = 5 * 60;  /* - * Reaper + * Reaper for unused keys. + */ +static void key_garbage_collector(struct work_struct *work); +DECLARE_WORK(key_gc_work, key_garbage_collector); + +/* + * Reaper for links from keyrings to dead keys.   */  static void key_gc_timer_func(unsigned long); -static void key_garbage_collector(struct work_struct *);  static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); -static DECLARE_WORK(key_gc_work, key_garbage_collector); -static key_serial_t key_gc_cursor; /* the last key the gc considered */ -static bool key_gc_again; -static unsigned long key_gc_executing; +  static time_t key_gc_next_run = LONG_MAX; -static time_t key_gc_new_timer; +static struct key_type *key_gc_dead_keytype; + +static unsigned long key_gc_flags; +#define KEY_GC_KEY_EXPIRED	0	/* A key expired and needs unlinking */ +#define KEY_GC_REAP_KEYTYPE	1	/* A keytype is being unregistered */ +#define KEY_GC_REAPING_KEYTYPE	2	/* Cleared when keytype reaped */ +  /* - * Schedule a garbage collection run - * - precision isn't particularly important + * Any key whose type gets unregistered will be re-typed to this if it can't be + * immediately unlinked. + */ +struct key_type key_type_dead = { +	.name = "dead", +}; + +/* + * Schedule a garbage collection run. + * - time precision isn't particularly important   */  void key_schedule_gc(time_t gc_at)  { @@ -42,181 +60,308 @@ void key_schedule_gc(time_t gc_at)  	kenter("%ld", gc_at - now); -	if (gc_at <= now) { +	if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) { +		kdebug("IMMEDIATE");  		schedule_work(&key_gc_work);  	} else if (gc_at < key_gc_next_run) { +		kdebug("DEFERRED"); +		key_gc_next_run = gc_at;  		expires = jiffies + (gc_at - now) * HZ;  		mod_timer(&key_gc_timer, expires);  	}  }  /* - * The garbage collector timer kicked off + * Schedule a dead links collection run. + */ +void key_schedule_gc_links(void) +{ +	set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); +	schedule_work(&key_gc_work); +} + +/* + * Some key's cleanup time was met after it expired, so we need to get the + * reaper to go through a cycle finding expired keys.   */  static void key_gc_timer_func(unsigned long data)  {  	kenter("");  	key_gc_next_run = LONG_MAX; +	key_schedule_gc_links(); +} + +/* + * wait_on_bit() sleep function for uninterruptible waiting + */ +static int key_gc_wait_bit(void *flags) +{ +	schedule(); +	return 0; +} + +/* + * Reap keys of dead type. + * + * We use three flags to make sure we see three complete cycles of the garbage + * collector: the first to mark keys of that type as being dead, the second to + * collect dead links and the third to clean up the dead keys.  We have to be + * careful as there may already be a cycle in progress. + * + * The caller must be holding key_types_sem. + */ +void key_gc_keytype(struct key_type *ktype) +{ +	kenter("%s", ktype->name); + +	key_gc_dead_keytype = ktype; +	set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags); +	smp_mb(); +	set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags); + +	kdebug("schedule");  	schedule_work(&key_gc_work); + +	kdebug("sleep"); +	wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, key_gc_wait_bit, +		    TASK_UNINTERRUPTIBLE); + +	key_gc_dead_keytype = NULL; +	kleave("");  }  /* - * Garbage collect pointers from a keyring - * - return true if we altered the keyring + * Garbage collect a list of unreferenced, detached keys   */ -static bool key_gc_keyring(struct key *keyring, time_t limit) -	__releases(key_serial_lock) +static noinline void key_gc_unused_keys(struct list_head *keys)  { -	struct keyring_list *klist; -	struct key *key; -	int loop; +	while (!list_empty(keys)) { +		struct key *key = +			list_entry(keys->next, struct key, graveyard_link); +		list_del(&key->graveyard_link); -	kenter("%x", key_serial(keyring)); +		kdebug("- %u", key->serial); +		key_check(key); -	if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) -		goto dont_gc; +		security_key_free(key); -	/* scan the keyring looking for dead keys */ -	rcu_read_lock(); -	klist = rcu_dereference(keyring->payload.subscriptions); -	if (!klist) -		goto unlock_dont_gc; +		/* deal with the user's key tracking and quota */ +		if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { +			spin_lock(&key->user->lock); +			key->user->qnkeys--; +			key->user->qnbytes -= key->quotalen; +			spin_unlock(&key->user->lock); +		} -	for (loop = klist->nkeys - 1; loop >= 0; loop--) { -		key = klist->keys[loop]; -		if (test_bit(KEY_FLAG_DEAD, &key->flags) || -		    (key->expiry > 0 && key->expiry <= limit)) -			goto do_gc; -	} +		atomic_dec(&key->user->nkeys); +		if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) +			atomic_dec(&key->user->nikeys); -unlock_dont_gc: -	rcu_read_unlock(); -dont_gc: -	kleave(" = false"); -	return false; +		key_user_put(key->user); -do_gc: -	rcu_read_unlock(); -	key_gc_cursor = keyring->serial; -	key_get(keyring); -	spin_unlock(&key_serial_lock); -	keyring_gc(keyring, limit); -	key_put(keyring); -	kleave(" = true"); -	return true; +		/* now throw away the key memory */ +		if (key->type->destroy) +			key->type->destroy(key); + +		kfree(key->description); + +#ifdef KEY_DEBUGGING +		key->magic = KEY_DEBUG_MAGIC_X; +#endif +		kmem_cache_free(key_jar, key); +	}  }  /* - * Garbage collector for keys - * - this involves scanning the keyrings for dead, expired and revoked keys - *   that have overstayed their welcome + * Garbage collector for unused keys. + * + * This is done in process context so that we don't have to disable interrupts + * all over the place.  key_put() schedules this rather than trying to do the + * cleanup itself, which means key_put() doesn't have to sleep.   */  static void key_garbage_collector(struct work_struct *work)  { -	struct rb_node *rb; -	key_serial_t cursor; -	struct key *key, *xkey; -	time_t new_timer = LONG_MAX, limit, now; - -	now = current_kernel_time().tv_sec; -	kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now); - -	if (test_and_set_bit(0, &key_gc_executing)) { -		key_schedule_gc(current_kernel_time().tv_sec + 1); -		kleave(" [busy; deferring]"); -		return; -	} +	static LIST_HEAD(graveyard); +	static u8 gc_state;		/* Internal persistent state */ +#define KEY_GC_REAP_AGAIN	0x01	/* - Need another cycle */ +#define KEY_GC_REAPING_LINKS	0x02	/* - We need to reap links */ +#define KEY_GC_SET_TIMER	0x04	/* - We need to restart the timer */ +#define KEY_GC_REAPING_DEAD_1	0x10	/* - We need to mark dead keys */ +#define KEY_GC_REAPING_DEAD_2	0x20	/* - We need to reap dead key links */ +#define KEY_GC_REAPING_DEAD_3	0x40	/* - We need to reap dead keys */ +#define KEY_GC_FOUND_DEAD_KEY	0x80	/* - We found at least one dead key */ -	limit = now; +	struct rb_node *cursor; +	struct key *key; +	time_t new_timer, limit; + +	kenter("[%lx,%x]", key_gc_flags, gc_state); + +	limit = current_kernel_time().tv_sec;  	if (limit > key_gc_delay)  		limit -= key_gc_delay;  	else  		limit = key_gc_delay; +	/* Work out what we're going to be doing in this pass */ +	gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2; +	gc_state <<= 1; +	if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags)) +		gc_state |= KEY_GC_REAPING_LINKS | KEY_GC_SET_TIMER; + +	if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) +		gc_state |= KEY_GC_REAPING_DEAD_1; +	kdebug("new pass %x", gc_state); + +	new_timer = LONG_MAX; + +	/* As only this function is permitted to remove things from the key +	 * serial tree, if cursor is non-NULL then it will always point to a +	 * valid node in the tree - even if lock got dropped. +	 */  	spin_lock(&key_serial_lock); +	cursor = rb_first(&key_serial_tree); -	if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) { -		spin_unlock(&key_serial_lock); -		clear_bit(0, &key_gc_executing); -		return; -	} +continue_scanning: +	while (cursor) { +		key = rb_entry(cursor, struct key, serial_node); +		cursor = rb_next(cursor); -	cursor = key_gc_cursor; -	if (cursor < 0) -		cursor = 0; -	if (cursor > 0) -		new_timer = key_gc_new_timer; -	else -		key_gc_again = false; - -	/* find the first key above the cursor */ -	key = NULL; -	rb = key_serial_tree.rb_node; -	while (rb) { -		xkey = rb_entry(rb, struct key, serial_node); -		if (cursor < xkey->serial) { -			key = xkey; -			rb = rb->rb_left; -		} else if (cursor > xkey->serial) { -			rb = rb->rb_right; -		} else { -			rb = rb_next(rb); -			if (!rb) -				goto reached_the_end; -			key = rb_entry(rb, struct key, serial_node); -			break; +		if (atomic_read(&key->usage) == 0) +			goto found_unreferenced_key; + +		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) { +			if (key->type == key_gc_dead_keytype) { +				gc_state |= KEY_GC_FOUND_DEAD_KEY; +				set_bit(KEY_FLAG_DEAD, &key->flags); +				key->perm = 0; +				goto skip_dead_key; +			}  		} -	} -	if (!key) -		goto reached_the_end; +		if (gc_state & KEY_GC_SET_TIMER) { +			if (key->expiry > limit && key->expiry < new_timer) { +				kdebug("will expire %x in %ld", +				       key_serial(key), key->expiry - limit); +				new_timer = key->expiry; +			} +		} + +		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) +			if (key->type == key_gc_dead_keytype) +				gc_state |= KEY_GC_FOUND_DEAD_KEY; -	/* trawl through the keys looking for keyrings */ -	for (;;) { -		if (key->expiry > limit && key->expiry < new_timer) { -			kdebug("will expire %x in %ld", -			       key_serial(key), key->expiry - limit); -			new_timer = key->expiry; +		if ((gc_state & KEY_GC_REAPING_LINKS) || +		    unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { +			if (key->type == &key_type_keyring) +				goto found_keyring;  		} -		if (key->type == &key_type_keyring && -		    key_gc_keyring(key, limit)) -			/* the gc had to release our lock so that the keyring -			 * could be modified, so we have to get it again */ -			goto gc_released_our_lock; +		if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) +			if (key->type == key_gc_dead_keytype) +				goto destroy_dead_key; -		rb = rb_next(&key->serial_node); -		if (!rb) -			goto reached_the_end; -		key = rb_entry(rb, struct key, serial_node); +	skip_dead_key: +		if (spin_is_contended(&key_serial_lock) || need_resched()) +			goto contended;  	} -gc_released_our_lock: -	kdebug("gc_released_our_lock"); -	key_gc_new_timer = new_timer; -	key_gc_again = true; -	clear_bit(0, &key_gc_executing); -	schedule_work(&key_gc_work); -	kleave(" [continue]"); -	return; - -	/* when we reach the end of the run, we set the timer for the next one */ -reached_the_end: -	kdebug("reached_the_end"); +contended:  	spin_unlock(&key_serial_lock); -	key_gc_new_timer = new_timer; -	key_gc_cursor = 0; -	clear_bit(0, &key_gc_executing); - -	if (key_gc_again) { -		/* there may have been a key that expired whilst we were -		 * scanning, so if we discarded any links we should do another -		 * scan */ -		new_timer = now + 1; -		key_schedule_gc(new_timer); -	} else if (new_timer < LONG_MAX) { + +maybe_resched: +	if (cursor) { +		cond_resched(); +		spin_lock(&key_serial_lock); +		goto continue_scanning; +	} + +	/* We've completed the pass.  Set the timer if we need to and queue a +	 * new cycle if necessary.  We keep executing cycles until we find one +	 * where we didn't reap any keys. +	 */ +	kdebug("pass complete"); + +	if (gc_state & KEY_GC_SET_TIMER && new_timer != (time_t)LONG_MAX) {  		new_timer += key_gc_delay;  		key_schedule_gc(new_timer);  	} -	kleave(" [end]"); + +	if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) || +	    !list_empty(&graveyard)) { +		/* Make sure that all pending keyring payload destructions are +		 * fulfilled and that people aren't now looking at dead or +		 * dying keys that they don't have a reference upon or a link +		 * to. +		 */ +		kdebug("gc sync"); +		synchronize_rcu(); +	} + +	if (!list_empty(&graveyard)) { +		kdebug("gc keys"); +		key_gc_unused_keys(&graveyard); +	} + +	if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 | +				 KEY_GC_REAPING_DEAD_2))) { +		if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) { +			/* No remaining dead keys: short circuit the remaining +			 * keytype reap cycles. +			 */ +			kdebug("dead short"); +			gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2); +			gc_state |= KEY_GC_REAPING_DEAD_3; +		} else { +			gc_state |= KEY_GC_REAP_AGAIN; +		} +	} + +	if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) { +		kdebug("dead wake"); +		smp_mb(); +		clear_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags); +		wake_up_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE); +	} + +	if (gc_state & KEY_GC_REAP_AGAIN) +		schedule_work(&key_gc_work); +	kleave(" [end %x]", gc_state); +	return; + +	/* We found an unreferenced key - once we've removed it from the tree, +	 * we can safely drop the lock. +	 */ +found_unreferenced_key: +	kdebug("unrefd key %d", key->serial); +	rb_erase(&key->serial_node, &key_serial_tree); +	spin_unlock(&key_serial_lock); + +	list_add_tail(&key->graveyard_link, &graveyard); +	gc_state |= KEY_GC_REAP_AGAIN; +	goto maybe_resched; + +	/* We found a keyring and we need to check the payload for links to +	 * dead or expired keys.  We don't flag another reap immediately as we +	 * have to wait for the old payload to be destroyed by RCU before we +	 * can reap the keys to which it refers. +	 */ +found_keyring: +	spin_unlock(&key_serial_lock); +	keyring_gc(key, limit); +	goto maybe_resched; + +	/* We found a dead key that is still referenced.  Reset its type and +	 * destroy its payload with its semaphore held. +	 */ +destroy_dead_key: +	spin_unlock(&key_serial_lock); +	kdebug("destroy key %d", key->serial); +	down_write(&key->sem); +	key->type = &key_type_dead; +	if (key_gc_dead_keytype->destroy) +		key_gc_dead_keytype->destroy(key); +	memset(&key->payload, KEY_DESTROY, sizeof(key->payload)); +	up_write(&key->sem); +	goto maybe_resched;  } diff --git a/security/keys/internal.h b/security/keys/internal.h index 56a133d8f37..5f20da01fd8 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -1,4 +1,4 @@ -/* internal.h: authentication token and access key management internal defs +/* Authentication token and access key management internal defs   *   * Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -14,6 +14,9 @@  #include <linux/sched.h>  #include <linux/key-type.h> +#include <linux/task_work.h> + +struct iovec;  #ifdef __KDEBUG  #define kenter(FMT, ...) \ @@ -31,14 +34,18 @@  	no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)  #endif +extern struct key_type key_type_dead;  extern struct key_type key_type_user; +extern struct key_type key_type_logon;  /*****************************************************************************/  /* - * keep track of keys for a user - * - this needs to be separate to user_struct to avoid a refcount-loop - *   (user_struct pins some keyrings which pin this struct) - * - this also keeps track of keys under request from userspace for this UID + * Keep track of keys for a user. + * + * This needs to be separate to user_struct to avoid a refcount-loop + * (user_struct pins some keyrings which pin this struct). + * + * We also keep track of keys under request from userspace for this UID here.   */  struct key_user {  	struct rb_node		node; @@ -47,8 +54,7 @@ struct key_user {  	atomic_t		usage;		/* for accessing qnkeys & qnbytes */  	atomic_t		nkeys;		/* number of keys */  	atomic_t		nikeys;		/* number of instantiated keys */ -	uid_t			uid; -	struct user_namespace	*user_ns; +	kuid_t			uid;  	int			qnkeys;		/* number of keys allocated to this user */  	int			qnbytes;	/* number of bytes allocated to this user */  }; @@ -57,12 +63,11 @@ extern struct rb_root	key_user_tree;  extern spinlock_t	key_user_lock;  extern struct key_user	root_key_user; -extern struct key_user *key_user_lookup(uid_t uid, -					struct user_namespace *user_ns); +extern struct key_user *key_user_lookup(kuid_t uid);  extern void key_user_put(struct key_user *user);  /* - * key quota limits + * Key quota limits.   * - root has its own separate limits to everyone else   */  extern unsigned key_quota_root_maxkeys; @@ -73,6 +78,7 @@ extern unsigned key_quota_maxbytes;  #define KEYQUOTA_LINK_BYTES	4		/* a link in a keyring is worth 4 bytes */ +extern struct kmem_cache *key_jar;  extern struct rb_root key_serial_tree;  extern spinlock_t key_serial_lock;  extern struct mutex key_construction_mutex; @@ -83,40 +89,53 @@ extern struct key_type *key_type_lookup(const char *type);  extern void key_type_put(struct key_type *ktype);  extern int __key_link_begin(struct key *keyring, -			    const struct key_type *type, -			    const char *description, -			    struct keyring_list **_prealloc); +			    const struct keyring_index_key *index_key, +			    struct assoc_array_edit **_edit);  extern int __key_link_check_live_key(struct key *keyring, struct key *key); -extern void __key_link(struct key *keyring, struct key *key, -		       struct keyring_list **_prealloc); +extern void __key_link(struct key *key, struct assoc_array_edit **_edit);  extern void __key_link_end(struct key *keyring, -			   struct key_type *type, -			   struct keyring_list *prealloc); +			   const struct keyring_index_key *index_key, +			   struct assoc_array_edit *edit); -extern key_ref_t __keyring_search_one(key_ref_t keyring_ref, -				      const struct key_type *type, -				      const char *description, -				      key_perm_t perm); +extern key_ref_t find_key_to_update(key_ref_t keyring_ref, +				    const struct keyring_index_key *index_key);  extern struct key *keyring_search_instkey(struct key *keyring,  					  key_serial_t target_id); +extern int iterate_over_keyring(const struct key *keyring, +				int (*func)(const struct key *key, void *data), +				void *data); +  typedef int (*key_match_func_t)(const struct key *, const void *); +struct keyring_search_context { +	struct keyring_index_key index_key; +	const struct cred	*cred; +	key_match_func_t	match; +	const void		*match_data; +	unsigned		flags; +#define KEYRING_SEARCH_LOOKUP_TYPE	0x0001	/* [as type->def_lookup_type] */ +#define KEYRING_SEARCH_NO_STATE_CHECK	0x0002	/* Skip state checks */ +#define KEYRING_SEARCH_DO_STATE_CHECK	0x0004	/* Override NO_STATE_CHECK */ +#define KEYRING_SEARCH_NO_UPDATE_TIME	0x0008	/* Don't update times */ +#define KEYRING_SEARCH_NO_CHECK_PERM	0x0010	/* Don't check permissions */ +#define KEYRING_SEARCH_DETECT_TOO_DEEP	0x0020	/* Give an error on excessive depth */ + +	int (*iterator)(const void *object, void *iterator_data); + +	/* Internal stuff */ +	int			skipped_ret; +	bool			possessed; +	key_ref_t		result; +	struct timespec		now; +}; +  extern 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); - -extern key_ref_t search_my_process_keyrings(struct key_type *type, -					    const void *description, -					    key_match_func_t match, -					    const struct cred *cred); -extern key_ref_t search_process_keyrings(struct key_type *type, -					 const void *description, -					 key_match_func_t match, -					 const struct cred *cred); +				    struct keyring_search_context *ctx); + +extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx); +extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);  extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check); @@ -141,34 +160,29 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,  #define KEY_LOOKUP_FOR_UNLINK	0x04  extern long join_session_keyring(const char *name); +extern void key_change_session_keyring(struct callback_head *twork); +extern struct work_struct key_gc_work;  extern unsigned key_gc_delay;  extern void keyring_gc(struct key *keyring, time_t limit); -extern void key_schedule_gc(time_t expiry_at); +extern void key_schedule_gc(time_t gc_at); +extern void key_schedule_gc_links(void); +extern void key_gc_keytype(struct key_type *ktype); -/* - * check to see whether permission is granted to use a key in the desired way - */  extern int key_task_permission(const key_ref_t key_ref,  			       const struct cred *cred,  			       key_perm_t perm); -static inline int key_permission(const key_ref_t key_ref, key_perm_t perm) +/* + * Check to see whether permission is granted to use a key in the desired way. + */ +static inline int key_permission(const key_ref_t key_ref, unsigned perm)  {  	return key_task_permission(key_ref, current_cred(), perm);  } -/* required permissions */ -#define	KEY_VIEW	0x01	/* require permission to view attributes */ -#define	KEY_READ	0x02	/* require permission to read content */ -#define	KEY_WRITE	0x04	/* require permission to update / modify */ -#define	KEY_SEARCH	0x08	/* require permission to search (keyring) or find (key) */ -#define	KEY_LINK	0x10	/* require permission to link */ -#define	KEY_SETATTR	0x20	/* require permission to change attributes */ -#define	KEY_ALL		0x3f	/* all the above permissions */ -  /* - * request_key authorisation + * Authorisation record for request_key().   */  struct request_key_auth {  	struct key		*target_key; @@ -188,7 +202,18 @@ extern struct key *request_key_auth_new(struct key *target,  extern struct key *key_get_instantiation_authkey(key_serial_t target_id);  /* - * keyctl functions + * Determine whether a key is dead. + */ +static inline bool key_is_dead(const struct key *key, time_t limit) +{ +	return +		key->flags & ((1 << KEY_FLAG_DEAD) | +			      (1 << KEY_FLAG_INVALIDATED)) || +		(key->expiry > 0 && key->expiry <= limit); +} + +/* + * keyctl() functions   */  extern long keyctl_get_keyring_ID(key_serial_t, int);  extern long keyctl_join_session_keyring(const char __user *); @@ -212,9 +237,27 @@ extern long keyctl_assume_authority(key_serial_t);  extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,  				size_t buflen);  extern long keyctl_session_to_parent(void); +extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t); +extern long keyctl_instantiate_key_iov(key_serial_t, +				       const struct iovec __user *, +				       unsigned, key_serial_t); +extern long keyctl_invalidate_key(key_serial_t); + +extern long keyctl_instantiate_key_common(key_serial_t, +					  const struct iovec *, +					  unsigned, size_t, key_serial_t); +#ifdef CONFIG_PERSISTENT_KEYRINGS +extern long keyctl_get_persistent(uid_t, key_serial_t); +extern unsigned persistent_keyring_expiry; +#else +static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) +{ +	return -EOPNOTSUPP; +} +#endif  /* - * debugging key validation + * Debugging key validation   */  #ifdef KEY_DEBUGGING  extern void __key_check(const struct key *); diff --git a/security/keys/key.c b/security/keys/key.c index c1eac8084ad..2048a110e7f 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -18,10 +18,9 @@  #include <linux/workqueue.h>  #include <linux/random.h>  #include <linux/err.h> -#include <linux/user_namespace.h>  #include "internal.h" -static struct kmem_cache	*key_jar; +struct kmem_cache *key_jar;  struct rb_root		key_serial_tree; /* tree of keys indexed by serial */  DEFINE_SPINLOCK(key_serial_lock); @@ -36,17 +35,9 @@ unsigned int key_quota_maxbytes = 20000;	/* general key space quota */  static LIST_HEAD(key_types_list);  static DECLARE_RWSEM(key_types_sem); -static void key_cleanup(struct work_struct *work); -static DECLARE_WORK(key_cleanup_task, key_cleanup); - -/* we serialise key instantiation and link */ +/* We serialise key instantiation and link */  DEFINE_MUTEX(key_construction_mutex); -/* any key who's type gets unegistered will be re-typed to this */ -static struct key_type key_type_dead = { -	.name		= "dead", -}; -  #ifdef KEY_DEBUGGING  void __key_check(const struct key *key)  { @@ -56,18 +47,17 @@ void __key_check(const struct key *key)  }  #endif -/*****************************************************************************/  /* - * get the key quota record for a user, allocating a new record if one doesn't - * already exist + * Get the key quota record for a user, allocating a new record if one doesn't + * already exist.   */ -struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns) +struct key_user *key_user_lookup(kuid_t uid)  {  	struct key_user *candidate = NULL, *user;  	struct rb_node *parent = NULL;  	struct rb_node **p; - try_again: +try_again:  	p = &key_user_tree.rb_node;  	spin_lock(&key_user_lock); @@ -76,13 +66,9 @@ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)  		parent = *p;  		user = rb_entry(parent, struct key_user, node); -		if (uid < user->uid) -			p = &(*p)->rb_left; -		else if (uid > user->uid) -			p = &(*p)->rb_right; -		else if (user_ns < user->user_ns) +		if (uid_lt(uid, user->uid))  			p = &(*p)->rb_left; -		else if (user_ns > user->user_ns) +		else if (uid_gt(uid, user->uid))  			p = &(*p)->rb_right;  		else  			goto found; @@ -111,7 +97,6 @@ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)  	atomic_set(&candidate->nkeys, 0);  	atomic_set(&candidate->nikeys, 0);  	candidate->uid = uid; -	candidate->user_ns = get_user_ns(user_ns);  	candidate->qnkeys = 0;  	candidate->qnbytes = 0;  	spin_lock_init(&candidate->lock); @@ -124,36 +109,30 @@ struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)  	goto out;  	/* okay - we found a user record for this UID */ - found: +found:  	atomic_inc(&user->usage);  	spin_unlock(&key_user_lock);  	kfree(candidate); - out: +out:  	return user; +} -} /* end key_user_lookup() */ - -/*****************************************************************************/  /* - * dispose of a user structure + * Dispose of a user structure   */  void key_user_put(struct key_user *user)  {  	if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {  		rb_erase(&user->node, &key_user_tree);  		spin_unlock(&key_user_lock); -		put_user_ns(user->user_ns);  		kfree(user);  	} +} -} /* end key_user_put() */ - -/*****************************************************************************/  /* - * assign a key the next unique serial number - * - these are assigned randomly to avoid security issues through covert - *   channel problems + * Allocate a serial number for a key.  These are assigned randomly to avoid + * security issues through covert channel problems.   */  static inline void key_alloc_serial(struct key *key)  { @@ -211,21 +190,39 @@ serial_exists:  		if (key->serial < xkey->serial)  			goto attempt_insertion;  	} +} -} /* end key_alloc_serial() */ - -/*****************************************************************************/ -/* - * allocate a key of the specified type - * - update the user's quota to reflect the existence of the key - * - called from a key-type operation with key_types_sem read-locked by - *   key_create_or_update() - *   - this prevents unregistration of the key type - * - upon return the key is as yet uninstantiated; the caller needs to either - *   instantiate the key or discard it before returning +/** + * key_alloc - Allocate a key of the specified type. + * @type: The type of key to allocate. + * @desc: The key description to allow the key to be searched out. + * @uid: The owner of the new key. + * @gid: The group ID for the new key's group permissions. + * @cred: The credentials specifying UID namespace. + * @perm: The permissions mask of the new key. + * @flags: Flags specifying quota properties. + * + * Allocate a key of the specified type with the attributes given.  The key is + * returned in an uninstantiated state and the caller needs to instantiate the + * key before returning. + * + * The user's key count quota is updated to reflect the creation of the key and + * the user's key data quota has the default for the key type reserved.  The + * instantiation function should amend this as necessary.  If insufficient + * quota is available, -EDQUOT will be returned. + * + * The LSM security modules can prevent a key being created, in which case + * -EACCES will be returned. + * + * Returns a pointer to the new key if successful and an error code otherwise. + * + * Note that the caller needs to ensure the key type isn't uninstantiated. + * Internally this can be done by locking key_types_sem.  Externally, this can + * be done by either never unregistering the key type, or making sure + * key_alloc() calls don't race with module unloading.   */  struct key *key_alloc(struct key_type *type, const char *desc, -		      uid_t uid, gid_t gid, const struct cred *cred, +		      kuid_t uid, kgid_t gid, const struct cred *cred,  		      key_perm_t perm, unsigned long flags)  {  	struct key_user *user = NULL; @@ -237,20 +234,28 @@ struct key *key_alloc(struct key_type *type, const char *desc,  	if (!desc || !*desc)  		goto error; -	desclen = strlen(desc) + 1; -	quotalen = desclen + type->def_datalen; +	if (type->vet_description) { +		ret = type->vet_description(desc); +		if (ret < 0) { +			key = ERR_PTR(ret); +			goto error; +		} +	} + +	desclen = strlen(desc); +	quotalen = desclen + 1 + type->def_datalen;  	/* get hold of the key tracking for this user */ -	user = key_user_lookup(uid, cred->user->user_ns); +	user = key_user_lookup(uid);  	if (!user)  		goto no_memory_1;  	/* check that the user's quota permits allocation of another key and  	 * its description */  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) { -		unsigned maxkeys = (uid == 0) ? +		unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?  			key_quota_root_maxkeys : key_quota_maxkeys; -		unsigned maxbytes = (uid == 0) ? +		unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?  			key_quota_root_maxbytes : key_quota_maxbytes;  		spin_lock(&user->lock); @@ -267,34 +272,32 @@ struct key *key_alloc(struct key_type *type, const char *desc,  	}  	/* allocate and initialise the key and its description */ -	key = kmem_cache_alloc(key_jar, GFP_KERNEL); +	key = kmem_cache_zalloc(key_jar, GFP_KERNEL);  	if (!key)  		goto no_memory_2;  	if (desc) { -		key->description = kmemdup(desc, desclen, GFP_KERNEL); +		key->index_key.desc_len = desclen; +		key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);  		if (!key->description)  			goto no_memory_3;  	}  	atomic_set(&key->usage, 1);  	init_rwsem(&key->sem); -	key->type = type; +	lockdep_set_class(&key->sem, &type->lock_class); +	key->index_key.type = type;  	key->user = user;  	key->quotalen = quotalen;  	key->datalen = type->def_datalen;  	key->uid = uid;  	key->gid = gid;  	key->perm = perm; -	key->flags = 0; -	key->expiry = 0; -	key->payload.data = NULL; -	key->security = NULL;  	if (!(flags & KEY_ALLOC_NOT_IN_QUOTA))  		key->flags |= 1 << KEY_FLAG_IN_QUOTA; - -	memset(&key->type_data, 0, sizeof(key->type_data)); +	if (flags & KEY_ALLOC_TRUSTED) +		key->flags |= 1 << KEY_FLAG_TRUSTED;  #ifdef KEY_DEBUGGING  	key->magic = KEY_DEBUG_MAGIC; @@ -344,14 +347,19 @@ no_quota:  	key_user_put(user);  	key = ERR_PTR(-EDQUOT);  	goto error; - -} /* end key_alloc() */ - +}  EXPORT_SYMBOL(key_alloc); -/*****************************************************************************/ -/* - * reserve an amount of quota for the key's payload +/** + * key_payload_reserve - Adjust data quota reservation for the key's payload + * @key: The key to make the reservation for. + * @datalen: The amount of data payload the caller now wants. + * + * Adjust the amount of the owning user's key data quota that a key reserves. + * If the amount is increased, then -EDQUOT may be returned if there isn't + * enough free quota available. + * + * If successful, 0 is returned.   */  int key_payload_reserve(struct key *key, size_t datalen)  { @@ -362,7 +370,7 @@ int key_payload_reserve(struct key *key, size_t datalen)  	/* contemplate the quota adjustment */  	if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { -		unsigned maxbytes = (key->user->uid == 0) ? +		unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ?  			key_quota_root_maxbytes : key_quota_maxbytes;  		spin_lock(&key->user->lock); @@ -384,22 +392,20 @@ int key_payload_reserve(struct key *key, size_t datalen)  		key->datalen = datalen;  	return ret; - -} /* end key_payload_reserve() */ - +}  EXPORT_SYMBOL(key_payload_reserve); -/*****************************************************************************/  /* - * instantiate a key and link it into the target keyring atomically - * - called with the target keyring's semaphore writelocked + * Instantiate a key and link it into the target keyring atomically.  Must be + * called with the target keyring's semaphore writelocked.  The target key's + * semaphore need not be locked as instantiation is serialised by + * key_construction_mutex.   */  static int __key_instantiate_and_link(struct key *key, -				      const void *data, -				      size_t datalen, +				      struct key_preparsed_payload *prep,  				      struct key *keyring,  				      struct key *authkey, -				      struct keyring_list **_prealloc) +				      struct assoc_array_edit **_edit)  {  	int ret, awaken; @@ -414,7 +420,7 @@ static int __key_instantiate_and_link(struct key *key,  	/* can't instantiate twice */  	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {  		/* instantiate the key */ -		ret = key->type->instantiate(key, data, datalen); +		ret = key->type->instantiate(key, prep);  		if (ret == 0) {  			/* mark the key as being instantiated */ @@ -426,7 +432,7 @@ static int __key_instantiate_and_link(struct key *key,  			/* and link it into the destination keyring */  			if (keyring) -				__key_link(keyring, key, _prealloc); +				__key_link(key, _edit);  			/* disable the authorisation key */  			if (authkey) @@ -441,12 +447,23 @@ static int __key_instantiate_and_link(struct key *key,  		wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);  	return ret; +} -} /* end __key_instantiate_and_link() */ - -/*****************************************************************************/ -/* - * instantiate a key and link it into the target keyring atomically +/** + * key_instantiate_and_link - Instantiate a key and link it into the keyring. + * @key: The key to instantiate. + * @data: The data to use to instantiate the keyring. + * @datalen: The length of @data. + * @keyring: Keyring to create a link in on success (or NULL). + * @authkey: The authorisation token permitting instantiation. + * + * Instantiate a key that's in the uninstantiated state using the provided data + * and, if successful, link it in to the destination keyring if one is + * supplied. + * + * If successful, 0 is returned, the authorisation token is revoked and anyone + * waiting for the key is woken up.  If the key was already instantiated, + * -EBUSY will be returned.   */  int key_instantiate_and_link(struct key *key,  			     const void *data, @@ -454,38 +471,68 @@ int key_instantiate_and_link(struct key *key,  			     struct key *keyring,  			     struct key *authkey)  { -	struct keyring_list *prealloc; +	struct key_preparsed_payload prep; +	struct assoc_array_edit *edit;  	int ret; +	memset(&prep, 0, sizeof(prep)); +	prep.data = data; +	prep.datalen = datalen; +	prep.quotalen = key->type->def_datalen; +	if (key->type->preparse) { +		ret = key->type->preparse(&prep); +		if (ret < 0) +			goto error; +	} +  	if (keyring) { -		ret = __key_link_begin(keyring, key->type, key->description, -				       &prealloc); +		ret = __key_link_begin(keyring, &key->index_key, &edit);  		if (ret < 0) -			return ret; +			goto error_free_preparse;  	} -	ret = __key_instantiate_and_link(key, data, datalen, keyring, authkey, -					 &prealloc); +	ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit);  	if (keyring) -		__key_link_end(keyring, key->type, prealloc); +		__key_link_end(keyring, &key->index_key, edit); +error_free_preparse: +	if (key->type->preparse) +		key->type->free_preparse(&prep); +error:  	return ret; - -} /* end key_instantiate_and_link() */ +}  EXPORT_SYMBOL(key_instantiate_and_link); -/*****************************************************************************/ -/* - * negatively instantiate a key and link it into the target keyring atomically +/** + * key_reject_and_link - Negatively instantiate a key and link it into the keyring. + * @key: The key to instantiate. + * @timeout: The timeout on the negative key. + * @error: The error to return when the key is hit. + * @keyring: Keyring to create a link in on success (or NULL). + * @authkey: The authorisation token permitting instantiation. + * + * Negatively instantiate a key that's in the uninstantiated state and, if + * successful, set its timeout and stored error and link it in to the + * destination keyring if one is supplied.  The key and any links to the key + * will be automatically garbage collected after the timeout expires. + * + * Negative keys are used to rate limit repeated request_key() calls by causing + * them to return the stored error code (typically ENOKEY) until the negative + * key expires. + * + * If successful, 0 is returned, the authorisation token is revoked and anyone + * waiting for the key is woken up.  If the key was already instantiated, + * -EBUSY will be returned.   */ -int key_negate_and_link(struct key *key, +int key_reject_and_link(struct key *key,  			unsigned timeout, +			unsigned error,  			struct key *keyring,  			struct key *authkey)  { -	struct keyring_list *prealloc; +	struct assoc_array_edit *edit;  	struct timespec now;  	int ret, awaken, link_ret = 0; @@ -496,8 +543,7 @@ int key_negate_and_link(struct key *key,  	ret = -EBUSY;  	if (keyring) -		link_ret = __key_link_begin(keyring, key->type, -					    key->description, &prealloc); +		link_ret = __key_link_begin(keyring, &key->index_key, &edit);  	mutex_lock(&key_construction_mutex); @@ -505,6 +551,8 @@ int key_negate_and_link(struct key *key,  	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {  		/* mark the key as being negatively instantiated */  		atomic_inc(&key->user->nikeys); +		key->type_data.reject_error = -error; +		smp_wmb();  		set_bit(KEY_FLAG_NEGATIVE, &key->flags);  		set_bit(KEY_FLAG_INSTANTIATED, &key->flags);  		now = current_kernel_time(); @@ -518,7 +566,7 @@ int key_negate_and_link(struct key *key,  		/* and link it into the destination keyring */  		if (keyring && link_ret == 0) -			__key_link(keyring, key, &prealloc); +			__key_link(key, &edit);  		/* disable the authorisation key */  		if (authkey) @@ -528,87 +576,23 @@ int key_negate_and_link(struct key *key,  	mutex_unlock(&key_construction_mutex);  	if (keyring) -		__key_link_end(keyring, key->type, prealloc); +		__key_link_end(keyring, &key->index_key, edit);  	/* wake up anyone waiting for a key to be constructed */  	if (awaken)  		wake_up_bit(&key->flags, KEY_FLAG_USER_CONSTRUCT);  	return ret == 0 ? link_ret : ret; +} +EXPORT_SYMBOL(key_reject_and_link); -} /* end key_negate_and_link() */ - -EXPORT_SYMBOL(key_negate_and_link); - -/*****************************************************************************/ -/* - * do cleaning up in process context so that we don't have to disable - * interrupts all over the place - */ -static void key_cleanup(struct work_struct *work) -{ -	struct rb_node *_n; -	struct key *key; - - go_again: -	/* look for a dead key in the tree */ -	spin_lock(&key_serial_lock); - -	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { -		key = rb_entry(_n, struct key, serial_node); - -		if (atomic_read(&key->usage) == 0) -			goto found_dead_key; -	} - -	spin_unlock(&key_serial_lock); -	return; - - found_dead_key: -	/* we found a dead key - once we've removed it from the tree, we can -	 * drop the lock */ -	rb_erase(&key->serial_node, &key_serial_tree); -	spin_unlock(&key_serial_lock); - -	key_check(key); - -	security_key_free(key); - -	/* deal with the user's key tracking and quota */ -	if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { -		spin_lock(&key->user->lock); -		key->user->qnkeys--; -		key->user->qnbytes -= key->quotalen; -		spin_unlock(&key->user->lock); -	} - -	atomic_dec(&key->user->nkeys); -	if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) -		atomic_dec(&key->user->nikeys); - -	key_user_put(key->user); - -	/* now throw away the key memory */ -	if (key->type->destroy) -		key->type->destroy(key); - -	kfree(key->description); - -#ifdef KEY_DEBUGGING -	key->magic = KEY_DEBUG_MAGIC_X; -#endif -	kmem_cache_free(key_jar, key); - -	/* there may, of course, be more than one key to destroy */ -	goto go_again; - -} /* end key_cleanup() */ - -/*****************************************************************************/ -/* - * dispose of a reference to a key - * - when all the references are gone, we schedule the cleanup task to come and - *   pull it out of the tree in definite process context +/** + * key_put - Discard a reference to a key. + * @key: The key to discard a reference from. + * + * Discard a reference to a key, and when all the references are gone, we + * schedule the cleanup task to come and pull it out of the tree in process + * context at some later time.   */  void key_put(struct key *key)  { @@ -616,16 +600,13 @@ void key_put(struct key *key)  		key_check(key);  		if (atomic_dec_and_test(&key->usage)) -			schedule_work(&key_cleanup_task); +			schedule_work(&key_gc_work);  	} - -} /* end key_put() */ - +}  EXPORT_SYMBOL(key_put); -/*****************************************************************************/  /* - * find a key by its serial number + * Find a key by its serial number.   */  struct key *key_lookup(key_serial_t id)  { @@ -647,11 +628,11 @@ struct key *key_lookup(key_serial_t id)  			goto found;  	} - not_found: +not_found:  	key = ERR_PTR(-ENOKEY);  	goto error; - found: +found:  	/* pretend it doesn't exist if it is awaiting deletion */  	if (atomic_read(&key->usage) == 0)  		goto not_found; @@ -659,18 +640,18 @@ struct key *key_lookup(key_serial_t id)  	/* this races with key_put(), but that doesn't matter since key_put()  	 * doesn't actually change the key  	 */ -	atomic_inc(&key->usage); +	__key_get(key); - error: +error:  	spin_unlock(&key_serial_lock);  	return key; +} -} /* end key_lookup() */ - -/*****************************************************************************/  /* - * find and lock the specified key type against removal - * - we return with the sem readlocked + * Find and lock the specified key type against removal. + * + * We return with the sem read-locked if successful.  If the type wasn't + * available -ENOKEY is returned instead.   */  struct key_type *key_type_lookup(const char *type)  { @@ -688,35 +669,52 @@ struct key_type *key_type_lookup(const char *type)  	up_read(&key_types_sem);  	ktype = ERR_PTR(-ENOKEY); - found_kernel_type: +found_kernel_type:  	return ktype; +} + +void key_set_timeout(struct key *key, unsigned timeout) +{ +	struct timespec now; +	time_t expiry = 0; + +	/* make the changes with the locks held to prevent races */ +	down_write(&key->sem); -} /* end key_type_lookup() */ +	if (timeout > 0) { +		now = current_kernel_time(); +		expiry = now.tv_sec + timeout; +	} + +	key->expiry = expiry; +	key_schedule_gc(key->expiry + key_gc_delay); + +	up_write(&key->sem); +} +EXPORT_SYMBOL_GPL(key_set_timeout); -/*****************************************************************************/  /* - * unlock a key type + * Unlock a key type locked by key_type_lookup().   */  void key_type_put(struct key_type *ktype)  {  	up_read(&key_types_sem); +} -} /* end key_type_put() */ - -/*****************************************************************************/  /* - * attempt to update an existing key - * - the key has an incremented refcount - * - we need to put the key if we get an error + * Attempt to update an existing key. + * + * The key is given to us with an incremented refcount that we need to discard + * if we get an error.   */  static inline key_ref_t __key_update(key_ref_t key_ref, -				     const void *payload, size_t plen) +				     struct key_preparsed_payload *prep)  {  	struct key *key = key_ref_to_ptr(key_ref);  	int ret;  	/* need write permission on the key to update it */ -	ret = key_permission(key_ref, KEY_WRITE); +	ret = key_permission(key_ref, KEY_NEED_WRITE);  	if (ret < 0)  		goto error; @@ -726,7 +724,7 @@ static inline key_ref_t __key_update(key_ref_t key_ref,  	down_write(&key->sem); -	ret = key->type->update(key, payload, plen); +	ret = key->type->update(key, prep);  	if (ret == 0)  		/* updating a negative key instantiates it */  		clear_bit(KEY_FLAG_NEGATIVE, &key->flags); @@ -742,13 +740,32 @@ error:  	key_put(key);  	key_ref = ERR_PTR(ret);  	goto out; +} -} /* end __key_update() */ - -/*****************************************************************************/ -/* - * search the specified keyring for a key of the same description; if one is - * found, update it, otherwise add a new one +/** + * key_create_or_update - Update or create and instantiate a key. + * @keyring_ref: A pointer to the destination keyring with possession flag. + * @type: The type of key. + * @description: The searchable description for the key. + * @payload: The data to use to instantiate or update the key. + * @plen: The length of @payload. + * @perm: The permissions mask for a new key. + * @flags: The quota flags for a new key. + * + * Search the destination keyring for a key of the same description and if one + * is found, update it, otherwise create and instantiate a new one and create a + * link to it from that keyring. + * + * If perm is KEY_PERM_UNDEF then an appropriate key permissions mask will be + * concocted. + * + * Returns a pointer to the new key if successful, -ENODEV if the key type + * wasn't available, -ENOTDIR if the keyring wasn't a keyring, -EACCES if the + * caller isn't permitted to modify the keyring or the LSM did not permit + * creation of the key. + * + * On success, the possession flag from the keyring ref will be tacked on to + * the key ref before it is returned.   */  key_ref_t key_create_or_update(key_ref_t keyring_ref,  			       const char *type, @@ -758,24 +775,28 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  			       key_perm_t perm,  			       unsigned long flags)  { -	struct keyring_list *prealloc; +	struct keyring_index_key index_key = { +		.description	= description, +	}; +	struct key_preparsed_payload prep; +	struct assoc_array_edit *edit;  	const struct cred *cred = current_cred(); -	struct key_type *ktype;  	struct key *keyring, *key = NULL;  	key_ref_t key_ref;  	int ret;  	/* look up the key type to see if it's one of the registered kernel  	 * types */ -	ktype = key_type_lookup(type); -	if (IS_ERR(ktype)) { +	index_key.type = key_type_lookup(type); +	if (IS_ERR(index_key.type)) {  		key_ref = ERR_PTR(-ENODEV);  		goto error;  	}  	key_ref = ERR_PTR(-EINVAL); -	if (!ktype->match || !ktype->instantiate) -		goto error_2; +	if (!index_key.type->match || !index_key.type->instantiate || +	    (!index_key.description && !index_key.type->preparse)) +		goto error_put_type;  	keyring = key_ref_to_ptr(keyring_ref); @@ -783,122 +804,173 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,  	key_ref = ERR_PTR(-ENOTDIR);  	if (keyring->type != &key_type_keyring) -		goto error_2; +		goto error_put_type; + +	memset(&prep, 0, sizeof(prep)); +	prep.data = payload; +	prep.datalen = plen; +	prep.quotalen = index_key.type->def_datalen; +	prep.trusted = flags & KEY_ALLOC_TRUSTED; +	if (index_key.type->preparse) { +		ret = index_key.type->preparse(&prep); +		if (ret < 0) { +			key_ref = ERR_PTR(ret); +			goto error_put_type; +		} +		if (!index_key.description) +			index_key.description = prep.description; +		key_ref = ERR_PTR(-EINVAL); +		if (!index_key.description) +			goto error_free_prep; +	} +	index_key.desc_len = strlen(index_key.description); -	ret = __key_link_begin(keyring, ktype, description, &prealloc); -	if (ret < 0) -		goto error_2; +	key_ref = ERR_PTR(-EPERM); +	if (!prep.trusted && test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags)) +		goto error_free_prep; +	flags |= prep.trusted ? KEY_ALLOC_TRUSTED : 0; + +	ret = __key_link_begin(keyring, &index_key, &edit); +	if (ret < 0) { +		key_ref = ERR_PTR(ret); +		goto error_free_prep; +	}  	/* if we're going to allocate a new key, we're going to have  	 * to modify the keyring */ -	ret = key_permission(keyring_ref, KEY_WRITE); +	ret = key_permission(keyring_ref, KEY_NEED_WRITE);  	if (ret < 0) {  		key_ref = ERR_PTR(ret); -		goto error_3; +		goto error_link_end;  	}  	/* if it's possible to update this type of key, search for an existing  	 * key of the same type and description in the destination keyring and  	 * update that instead if possible  	 */ -	if (ktype->update) { -		key_ref = __keyring_search_one(keyring_ref, ktype, description, -					       0); -		if (!IS_ERR(key_ref)) +	if (index_key.type->update) { +		key_ref = find_key_to_update(keyring_ref, &index_key); +		if (key_ref)  			goto found_matching_key;  	}  	/* if the client doesn't provide, decide on the permissions we want */  	if (perm == KEY_PERM_UNDEF) {  		perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR; -		perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK | KEY_USR_SETATTR; +		perm |= KEY_USR_VIEW; -		if (ktype->read) -			perm |= KEY_POS_READ | KEY_USR_READ; +		if (index_key.type->read) +			perm |= KEY_POS_READ; -		if (ktype == &key_type_keyring || ktype->update) -			perm |= KEY_USR_WRITE; +		if (index_key.type == &key_type_keyring || +		    index_key.type->update) +			perm |= KEY_POS_WRITE;  	}  	/* allocate a new key */ -	key = key_alloc(ktype, description, cred->fsuid, cred->fsgid, cred, -			perm, flags); +	key = key_alloc(index_key.type, index_key.description, +			cred->fsuid, cred->fsgid, cred, perm, flags);  	if (IS_ERR(key)) {  		key_ref = ERR_CAST(key); -		goto error_3; +		goto error_link_end;  	}  	/* instantiate it and link it into the target keyring */ -	ret = __key_instantiate_and_link(key, payload, plen, keyring, NULL, -					 &prealloc); +	ret = __key_instantiate_and_link(key, &prep, keyring, NULL, &edit);  	if (ret < 0) {  		key_put(key);  		key_ref = ERR_PTR(ret); -		goto error_3; +		goto error_link_end;  	}  	key_ref = make_key_ref(key, is_key_possessed(keyring_ref)); - error_3: -	__key_link_end(keyring, ktype, prealloc); - error_2: -	key_type_put(ktype); - error: +error_link_end: +	__key_link_end(keyring, &index_key, edit); +error_free_prep: +	if (index_key.type->preparse) +		index_key.type->free_preparse(&prep); +error_put_type: +	key_type_put(index_key.type); +error:  	return key_ref;   found_matching_key:  	/* we found a matching key, so we're going to try to update it  	 * - we can drop the locks first as we have the key pinned  	 */ -	__key_link_end(keyring, ktype, prealloc); -	key_type_put(ktype); - -	key_ref = __key_update(key_ref, payload, plen); -	goto error; - -} /* end key_create_or_update() */ +	__key_link_end(keyring, &index_key, edit); +	key_ref = __key_update(key_ref, &prep); +	goto error_free_prep; +}  EXPORT_SYMBOL(key_create_or_update); -/*****************************************************************************/ -/* - * update a key +/** + * key_update - Update a key's contents. + * @key_ref: The pointer (plus possession flag) to the key. + * @payload: The data to be used to update the key. + * @plen: The length of @payload. + * + * Attempt to update the contents of a key with the given payload data.  The + * caller must be granted Write permission on the key.  Negative keys can be + * instantiated by this method. + * + * Returns 0 on success, -EACCES if not permitted and -EOPNOTSUPP if the key + * type does not support updating.  The key type may return other errors.   */  int key_update(key_ref_t key_ref, const void *payload, size_t plen)  { +	struct key_preparsed_payload prep;  	struct key *key = key_ref_to_ptr(key_ref);  	int ret;  	key_check(key);  	/* the key must be writable */ -	ret = key_permission(key_ref, KEY_WRITE); +	ret = key_permission(key_ref, KEY_NEED_WRITE);  	if (ret < 0)  		goto error;  	/* attempt to update it if supported */  	ret = -EOPNOTSUPP; -	if (key->type->update) { -		down_write(&key->sem); - -		ret = key->type->update(key, payload, plen); -		if (ret == 0) -			/* updating a negative key instantiates it */ -			clear_bit(KEY_FLAG_NEGATIVE, &key->flags); +	if (!key->type->update) +		goto error; -		up_write(&key->sem); +	memset(&prep, 0, sizeof(prep)); +	prep.data = payload; +	prep.datalen = plen; +	prep.quotalen = key->type->def_datalen; +	if (key->type->preparse) { +		ret = key->type->preparse(&prep); +		if (ret < 0) +			goto error;  	} - error: -	return ret; +	down_write(&key->sem); -} /* end key_update() */ +	ret = key->type->update(key, &prep); +	if (ret == 0) +		/* updating a negative key instantiates it */ +		clear_bit(KEY_FLAG_NEGATIVE, &key->flags); +	up_write(&key->sem); + +	if (key->type->preparse) +		key->type->free_preparse(&prep); +error: +	return ret; +}  EXPORT_SYMBOL(key_update); -/*****************************************************************************/ -/* - * revoke a key +/** + * key_revoke - Revoke a key. + * @key: The key to be revoked. + * + * Mark a key as being revoked and ask the type to free up its resources.  The + * revocation timeout is set and the key and all its links will be + * automatically garbage collected after key_gc_delay amount of time if they + * are not manually dealt with first.   */  void key_revoke(struct key *key)  { @@ -926,20 +998,46 @@ void key_revoke(struct key *key)  	}  	up_write(&key->sem); +} +EXPORT_SYMBOL(key_revoke); -} /* end key_revoke() */ +/** + * key_invalidate - Invalidate a key. + * @key: The key to be invalidated. + * + * Mark a key as being invalidated and have it cleaned up immediately.  The key + * is ignored by all searches and other operations from this point. + */ +void key_invalidate(struct key *key) +{ +	kenter("%d", key_serial(key)); -EXPORT_SYMBOL(key_revoke); +	key_check(key); -/*****************************************************************************/ -/* - * register a type of key +	if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) { +		down_write_nested(&key->sem, 1); +		if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) +			key_schedule_gc_links(); +		up_write(&key->sem); +	} +} +EXPORT_SYMBOL(key_invalidate); + +/** + * register_key_type - Register a type of key. + * @ktype: The new key type. + * + * Register a new key type. + * + * Returns 0 on success or -EEXIST if a type of this name already exists.   */  int register_key_type(struct key_type *ktype)  {  	struct key_type *p;  	int ret; +	memset(&ktype->lock_class, 0, sizeof(ktype->lock_class)); +  	ret = -EEXIST;  	down_write(&key_types_sem); @@ -951,73 +1049,37 @@ int register_key_type(struct key_type *ktype)  	/* store the type */  	list_add(&ktype->link, &key_types_list); + +	pr_notice("Key type %s registered\n", ktype->name);  	ret = 0; - out: +out:  	up_write(&key_types_sem);  	return ret; - -} /* end register_key_type() */ - +}  EXPORT_SYMBOL(register_key_type); -/*****************************************************************************/ -/* - * unregister a type of key +/** + * unregister_key_type - Unregister a type of key. + * @ktype: The key type. + * + * Unregister a key type and mark all the extant keys of this type as dead. + * Those keys of this type are then destroyed to get rid of their payloads and + * they and their links will be garbage collected as soon as possible.   */  void unregister_key_type(struct key_type *ktype)  { -	struct rb_node *_n; -	struct key *key; -  	down_write(&key_types_sem); - -	/* withdraw the key type */  	list_del_init(&ktype->link); - -	/* mark all the keys of this type dead */ -	spin_lock(&key_serial_lock); - -	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { -		key = rb_entry(_n, struct key, serial_node); - -		if (key->type == ktype) { -			key->type = &key_type_dead; -			set_bit(KEY_FLAG_DEAD, &key->flags); -		} -	} - -	spin_unlock(&key_serial_lock); - -	/* make sure everyone revalidates their keys */ -	synchronize_rcu(); - -	/* we should now be able to destroy the payloads of all the keys of -	 * this type with impunity */ -	spin_lock(&key_serial_lock); - -	for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { -		key = rb_entry(_n, struct key, serial_node); - -		if (key->type == ktype) { -			if (ktype->destroy) -				ktype->destroy(key); -			memset(&key->payload, KEY_DESTROY, sizeof(key->payload)); -		} -	} - -	spin_unlock(&key_serial_lock); -	up_write(&key_types_sem); - -	key_schedule_gc(0); - -} /* end unregister_key_type() */ - +	downgrade_write(&key_types_sem); +	key_gc_keytype(ktype); +	pr_notice("Key type %s unregistered\n", ktype->name); +	up_read(&key_types_sem); +}  EXPORT_SYMBOL(unregister_key_type); -/*****************************************************************************/  /* - * initialise the key management stuff + * Initialise the key management state.   */  void __init key_init(void)  { @@ -1029,6 +1091,7 @@ void __init key_init(void)  	list_add_tail(&key_type_keyring.link, &key_types_list);  	list_add_tail(&key_type_dead.link, &key_types_list);  	list_add_tail(&key_type_user.link, &key_types_list); +	list_add_tail(&key_type_logon.link, &key_types_list);  	/* record the root user tracking */  	rb_link_node(&root_key_user.node, @@ -1037,5 +1100,4 @@ void __init key_init(void)  	rb_insert_color(&root_key_user.node,  			&key_user_tree); - -} /* end key_init() */ +} diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 60924f6a52d..cd5bd0cef25 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1,4 +1,4 @@ -/* keyctl.c: userspace keyctl operations +/* Userspace key control operations   *   * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -14,6 +14,7 @@  #include <linux/sched.h>  #include <linux/slab.h>  #include <linux/syscalls.h> +#include <linux/key.h>  #include <linux/keyctl.h>  #include <linux/fs.h>  #include <linux/capability.h> @@ -21,6 +22,7 @@  #include <linux/err.h>  #include <linux/vmalloc.h>  #include <linux/security.h> +#include <linux/uio.h>  #include <asm/uaccess.h>  #include "internal.h" @@ -31,28 +33,27 @@ static int key_get_type_from_user(char *type,  	int ret;  	ret = strncpy_from_user(type, _type, len); -  	if (ret < 0)  		return ret; -  	if (ret == 0 || ret >= len)  		return -EINVAL; -  	if (type[0] == '.')  		return -EPERM; -  	type[len - 1] = '\0'; -  	return 0;  } -/*****************************************************************************/  /* - * extract the description of a new key from userspace and either add it as a - * new key to the specified keyring or update a matching key in that keyring - * - the keyring must be writable - * - returns the new key's serial number - * - implements add_key() + * Extract the description of a new key from userspace and either add it as a + * new key to the specified keyring or update a matching key in that keyring. + * + * If the description is NULL or an empty string, the key type is asked to + * generate one from the payload. + * + * The keyring must be writable so that we can attach the key to it. + * + * If successful, the new key's serial number is returned, otherwise an error + * code is returned.   */  SYSCALL_DEFINE5(add_key, const char __user *, _type,  		const char __user *, _description, @@ -75,10 +76,17 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	if (ret < 0)  		goto error; -	description = strndup_user(_description, PAGE_SIZE); -	if (IS_ERR(description)) { -		ret = PTR_ERR(description); -		goto error; +	description = NULL; +	if (_description) { +		description = strndup_user(_description, PAGE_SIZE); +		if (IS_ERR(description)) { +			ret = PTR_ERR(description); +			goto error; +		} +		if (!*description) { +			kfree(description); +			description = NULL; +		}  	}  	/* pull the payload in if one was supplied */ @@ -87,7 +95,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	vm = false;  	if (_payload) {  		ret = -ENOMEM; -		payload = kmalloc(plen, GFP_KERNEL); +		payload = kmalloc(plen, GFP_KERNEL | __GFP_NOWARN);  		if (!payload) {  			if (plen <= PAGE_SIZE)  				goto error2; @@ -103,7 +111,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	}  	/* find the target keyring (which must be writable) */ -	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error3; @@ -132,19 +140,20 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,  	kfree(description);   error:  	return ret; +} -} /* end sys_add_key() */ - -/*****************************************************************************/  /* - * search the process keyrings for a matching key - * - nested keyrings may also be searched if they have Search permission - * - if a key is found, it will be attached to the destination keyring if - *   there's one specified - * - /sbin/request-key will be invoked if _callout_info is non-NULL - *   - the _callout_info string will be passed to /sbin/request-key - *   - if the _callout_info string is empty, it will be rendered as "-" - * - implements request_key() + * Search the process keyrings and keyring trees linked from those for a + * matching key.  Keyrings must have appropriate Search permission to be + * searched. + * + * If a key is found, it will be attached to the destination keyring if there's + * one specified and the serial number of the key will be returned. + * + * If no key is found, /sbin/request-key will be invoked if _callout_info is + * non-NULL in an attempt to create a key.  The _callout_info string will be + * passed to /sbin/request-key to aid with completing the request.  If the + * _callout_info string is "" then it will be changed to "-".   */  SYSCALL_DEFINE4(request_key, const char __user *, _type,  		const char __user *, _description, @@ -186,7 +195,7 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,  	dest_ref = NULL;  	if (destringid) {  		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, -					   KEY_WRITE); +					   KEY_NEED_WRITE);  		if (IS_ERR(dest_ref)) {  			ret = PTR_ERR(dest_ref);  			goto error3; @@ -209,8 +218,14 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,  		goto error5;  	} +	/* wait for the key to finish being constructed */ +	ret = wait_for_key_construction(key, 1); +	if (ret < 0) +		goto error6; +  	ret = key->serial; +error6:   	key_put(key);  error5:  	key_type_put(ktype); @@ -222,14 +237,14 @@ error2:  	kfree(description);  error:  	return ret; +} -} /* end sys_request_key() */ - -/*****************************************************************************/  /* - * get the ID of the specified process keyring - * - the keyring must have search permission to be found - * - implements keyctl(KEYCTL_GET_KEYRING_ID) + * Get the ID of the specified process keyring. + * + * The requested keyring must have search permission to be found. + * + * If successful, the ID of the requested keyring will be returned.   */  long keyctl_get_keyring_ID(key_serial_t id, int create)  { @@ -238,7 +253,7 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)  	long ret;  	lflags = create ? KEY_LOOKUP_CREATE : 0; -	key_ref = lookup_user_key(id, lflags, KEY_SEARCH); +	key_ref = lookup_user_key(id, lflags, KEY_NEED_SEARCH);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -248,13 +263,17 @@ long keyctl_get_keyring_ID(key_serial_t id, int create)  	key_ref_put(key_ref);  error:  	return ret; +} -} /* end keyctl_get_keyring_ID() */ - -/*****************************************************************************/  /* - * join the session keyring - * - implements keyctl(KEYCTL_JOIN_SESSION_KEYRING) + * Join a (named) session keyring. + * + * Create and join an anonymous session keyring or join a named session + * keyring, creating it if necessary.  A named session keyring must have Search + * permission for it to be joined.  Session keyrings without this permit will + * be skipped over. + * + * If successful, the ID of the joined session keyring will be returned.   */  long keyctl_join_session_keyring(const char __user *_name)  { @@ -277,14 +296,17 @@ long keyctl_join_session_keyring(const char __user *_name)  error:  	return ret; +} -} /* end keyctl_join_session_keyring() */ - -/*****************************************************************************/  /* - * update a key's data payload - * - the key must be writable - * - implements keyctl(KEYCTL_UPDATE) + * Update a key's data payload from the given data. + * + * The key must grant the caller Write permission and the key type must support + * updating for this to work.  A negative key can be positively instantiated + * with this call. + * + * If successful, 0 will be returned.  If the key type does not support + * updating, then -EOPNOTSUPP will be returned.   */  long keyctl_update_key(key_serial_t id,  		       const void __user *_payload, @@ -312,7 +334,7 @@ long keyctl_update_key(key_serial_t id,  	}  	/* find the target key (which must be writable) */ -	key_ref = lookup_user_key(id, 0, KEY_WRITE); +	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -326,26 +348,29 @@ error2:  	kfree(payload);  error:  	return ret; +} -} /* end keyctl_update_key() */ - -/*****************************************************************************/  /* - * revoke a key - * - the key must be writable - * - implements keyctl(KEYCTL_REVOKE) + * Revoke a key. + * + * The key must be grant the caller Write or Setattr permission for this to + * work.  The key type should give up its quota claim when revoked.  The key + * and any links to the key will be automatically garbage collected after a + * certain amount of time (/proc/sys/kernel/keys/gc_delay). + * + * If successful, 0 is returned.   */  long keyctl_revoke_key(key_serial_t id)  {  	key_ref_t key_ref;  	long ret; -	key_ref = lookup_user_key(id, 0, KEY_WRITE); +	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		if (ret != -EACCES)  			goto error; -		key_ref = lookup_user_key(id, 0, KEY_SETATTR); +		key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);  		if (IS_ERR(key_ref)) {  			ret = PTR_ERR(key_ref);  			goto error; @@ -358,53 +383,100 @@ long keyctl_revoke_key(key_serial_t id)  	key_ref_put(key_ref);  error:  	return ret; +} + +/* + * Invalidate a key. + * + * The key must be grant the caller Invalidate permission for this to work. + * The key and any links to the key will be automatically garbage collected + * immediately. + * + * If successful, 0 is returned. + */ +long keyctl_invalidate_key(key_serial_t id) +{ +	key_ref_t key_ref; +	long ret; + +	kenter("%d", id); + +	key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); +	if (IS_ERR(key_ref)) { +		ret = PTR_ERR(key_ref); +		goto error; +	} -} /* end keyctl_revoke_key() */ +	key_invalidate(key_ref_to_ptr(key_ref)); +	ret = 0; + +	key_ref_put(key_ref); +error: +	kleave(" = %ld", ret); +	return ret; +} -/*****************************************************************************/  /* - * clear the specified process keyring - * - the keyring must be writable - * - implements keyctl(KEYCTL_CLEAR) + * Clear the specified keyring, creating an empty process keyring if one of the + * special keyring IDs is used. + * + * The keyring must grant the caller Write permission for this to work.  If + * successful, 0 will be returned.   */  long keyctl_keyring_clear(key_serial_t ringid)  {  	key_ref_t keyring_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref); + +		/* Root is permitted to invalidate certain special keyrings */ +		if (capable(CAP_SYS_ADMIN)) { +			keyring_ref = lookup_user_key(ringid, 0, 0); +			if (IS_ERR(keyring_ref)) +				goto error; +			if (test_bit(KEY_FLAG_ROOT_CAN_CLEAR, +				     &key_ref_to_ptr(keyring_ref)->flags)) +				goto clear; +			goto error_put; +		} +  		goto error;  	} +clear:  	ret = keyring_clear(key_ref_to_ptr(keyring_ref)); - +error_put:  	key_ref_put(keyring_ref);  error:  	return ret; +} -} /* end keyctl_keyring_clear() */ - -/*****************************************************************************/  /* - * link a key into a keyring - * - the keyring must be writable - * - the key must be linkable - * - implements keyctl(KEYCTL_LINK) + * Create a link from a keyring to a key if there's no matching key in the + * keyring, otherwise replace the link to the matching key with a link to the + * new key. + * + * The key must grant the caller Link permission and the the keyring must grant + * the caller Write permission.  Furthermore, if an additional link is created, + * the keyring's quota will be extended. + * + * If successful, 0 will be returned.   */  long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)  {  	key_ref_t keyring_ref, key_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error;  	} -	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK); +	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error2; @@ -417,22 +489,23 @@ error2:  	key_ref_put(keyring_ref);  error:  	return ret; +} -} /* end keyctl_keyring_link() */ - -/*****************************************************************************/  /* - * unlink the first attachment of a key from a keyring - * - the keyring must be writable - * - we don't need any permissions on the key - * - implements keyctl(KEYCTL_UNLINK) + * Unlink a key from a keyring. + * + * The keyring must grant the caller Write permission for this to work; the key + * itself need not grant the caller anything.  If the last link to a key is + * removed then that key will be scheduled for destruction. + * + * If successful, 0 will be returned.   */  long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)  {  	key_ref_t keyring_ref, key_ref;  	long ret; -	keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE); +	keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error; @@ -451,19 +524,20 @@ error2:  	key_ref_put(keyring_ref);  error:  	return ret; +} -} /* end keyctl_keyring_unlink() */ - -/*****************************************************************************/  /* - * describe a user key - * - the key must have view permission - * - if there's a buffer, we place up to buflen bytes of data into it - * - unless there's an error, we return the amount of description available, - *   irrespective of how much we may have copied - * - the description is formatted thus: + * Return a description of a key to userspace. + * + * The key must grant the caller View permission for this to work. + * + * If there's a buffer, we place up to buflen bytes of data into it formatted + * in the following way: + *   *	type;uid;gid;perm;description<NUL> - * - implements keyctl(KEYCTL_DESCRIBE) + * + * If successful, we return the amount of description available, irrespective + * of how much we may have copied into the buffer.   */  long keyctl_describe_key(key_serial_t keyid,  			 char __user *buffer, @@ -474,7 +548,7 @@ long keyctl_describe_key(key_serial_t keyid,  	char *tmpbuf;  	long ret; -	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW); +	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);  	if (IS_ERR(key_ref)) {  		/* viewing a key under construction is permitted if we have the  		 * authorisation token handy */ @@ -506,8 +580,8 @@ okay:  	ret = snprintf(tmpbuf, PAGE_SIZE - 1,  		       "%s;%d;%d;%08x;%s",  		       key->type->name, -		       key->uid, -		       key->gid, +		       from_kuid_munged(current_user_ns(), key->uid), +		       from_kgid_munged(current_user_ns(), key->gid),  		       key->perm,  		       key->description ?: ""); @@ -531,18 +605,17 @@ error2:  	key_ref_put(key_ref);  error:  	return ret; +} -} /* end keyctl_describe_key() */ - -/*****************************************************************************/  /* - * search the specified keyring for a matching key - * - the start keyring must be searchable - * - nested keyrings may also be searched if they are searchable - * - only keys with search permission may be found - * - if a key is found, it will be attached to the destination keyring if - *   there's one specified - * - implements keyctl(KEYCTL_SEARCH) + * Search the specified keyring and any keyrings it links to for a matching + * key.  Only keyrings that grant the caller Search permission will be searched + * (this includes the starting keyring).  Only keys with Search permission can + * be found. + * + * If successful, the found key will be linked to the destination keyring if + * supplied and the key has Link permission, and the found key ID will be + * returned.   */  long keyctl_keyring_search(key_serial_t ringid,  			   const char __user *_type, @@ -566,7 +639,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	}  	/* get the keyring at which to begin the search */ -	keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH); +	keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_SEARCH);  	if (IS_ERR(keyring_ref)) {  		ret = PTR_ERR(keyring_ref);  		goto error2; @@ -576,7 +649,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	dest_ref = NULL;  	if (destringid) {  		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE, -					   KEY_WRITE); +					   KEY_NEED_WRITE);  		if (IS_ERR(dest_ref)) {  			ret = PTR_ERR(dest_ref);  			goto error3; @@ -603,7 +676,7 @@ long keyctl_keyring_search(key_serial_t ringid,  	/* link the resulting key to the destination keyring if we can */  	if (dest_ref) { -		ret = key_permission(key_ref, KEY_LINK); +		ret = key_permission(key_ref, KEY_NEED_LINK);  		if (ret < 0)  			goto error6; @@ -626,18 +699,17 @@ error2:  	kfree(description);  error:  	return ret; +} -} /* end keyctl_keyring_search() */ - -/*****************************************************************************/  /* - * read a user key's payload - * - the keyring must be readable or the key must be searchable from the - *   process's keyrings - * - if there's a buffer, we place up to buflen bytes of data into it - * - unless there's an error, we return the amount of data in the key, - *   irrespective of how much we may have copied - * - implements keyctl(KEYCTL_READ) + * Read a key's payload. + * + * The key must either grant the caller Read permission, or it must grant the + * caller Search permission when searched for from the process keyrings. + * + * If successful, we place up to buflen bytes of data into the buffer, if one + * is provided, and return the amount of data that is available in the key, + * irrespective of how much we copied into the buffer.   */  long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)  { @@ -655,7 +727,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)  	key = key_ref_to_ptr(key_ref);  	/* see if we can read it directly */ -	ret = key_permission(key_ref, KEY_READ); +	ret = key_permission(key_ref, KEY_NEED_READ);  	if (ret == 0)  		goto can_read_key;  	if (ret != -EACCES) @@ -688,29 +760,46 @@ error2:  	key_put(key);  error:  	return ret; +} -} /* end keyctl_read_key() */ - -/*****************************************************************************/  /* - * change the ownership of a key - * - the keyring owned by the changer - * - if the uid or gid is -1, then that parameter is not changed - * - implements keyctl(KEYCTL_CHOWN) + * Change the ownership of a key + * + * The key must grant the caller Setattr permission for this to work, though + * the key need not be fully instantiated yet.  For the UID to be changed, or + * for the GID to be changed to a group the caller is not a member of, the + * caller must have sysadmin capability.  If either uid or gid is -1 then that + * attribute is not changed. + * + * If the UID is to be changed, the new user must have sufficient quota to + * accept the key.  The quota deduction will be removed from the old user to + * the new user should the attribute be changed. + * + * If successful, 0 will be returned.   */ -long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) +long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)  {  	struct key_user *newowner, *zapowner = NULL;  	struct key *key;  	key_ref_t key_ref;  	long ret; +	kuid_t uid; +	kgid_t gid; + +	uid = make_kuid(current_user_ns(), user); +	gid = make_kgid(current_user_ns(), group); +	ret = -EINVAL; +	if ((user != (uid_t) -1) && !uid_valid(uid)) +		goto error; +	if ((group != (gid_t) -1) && !gid_valid(gid)) +		goto error;  	ret = 0; -	if (uid == (uid_t) -1 && gid == (gid_t) -1) +	if (user == (uid_t) -1 && group == (gid_t) -1)  		goto error;  	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, -				  KEY_SETATTR); +				  KEY_NEED_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -724,27 +813,27 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)  	if (!capable(CAP_SYS_ADMIN)) {  		/* only the sysadmin can chown a key to some other UID */ -		if (uid != (uid_t) -1 && key->uid != uid) +		if (user != (uid_t) -1 && !uid_eq(key->uid, uid))  			goto error_put;  		/* only the sysadmin can set the key's GID to a group other  		 * than one of those that the current process subscribes to */ -		if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid)) +		if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid))  			goto error_put;  	}  	/* change the UID */ -	if (uid != (uid_t) -1 && uid != key->uid) { +	if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) {  		ret = -ENOMEM; -		newowner = key_user_lookup(uid, current_user_ns()); +		newowner = key_user_lookup(uid);  		if (!newowner)  			goto error_put;  		/* transfer the quota burden to the new user */  		if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { -			unsigned maxkeys = (uid == 0) ? +			unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?  				key_quota_root_maxkeys : key_quota_maxkeys; -			unsigned maxbytes = (uid == 0) ? +			unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?  				key_quota_root_maxbytes : key_quota_maxbytes;  			spin_lock(&newowner->lock); @@ -778,7 +867,7 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)  	}  	/* change the GID */ -	if (gid != (gid_t) -1) +	if (group != (gid_t) -1)  		key->gid = gid;  	ret = 0; @@ -796,14 +885,14 @@ quota_overrun:  	zapowner = newowner;  	ret = -EDQUOT;  	goto error_put; +} -} /* end keyctl_chown_key() */ - -/*****************************************************************************/  /* - * change the permission mask on a key - * - the keyring owned by the changer - * - implements keyctl(KEYCTL_SETPERM) + * Change the permission mask on a key. + * + * The key must grant the caller Setattr permission for this to work, though + * the key need not be fully instantiated yet.  If the caller does not have + * sysadmin capability, it may only change the permission on keys that it owns.   */  long keyctl_setperm_key(key_serial_t id, key_perm_t perm)  { @@ -816,7 +905,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)  		goto error;  	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, -				  KEY_SETATTR); +				  KEY_NEED_SETATTR);  	if (IS_ERR(key_ref)) {  		ret = PTR_ERR(key_ref);  		goto error; @@ -829,7 +918,7 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)  	down_write(&key->sem);  	/* if we're not the sysadmin, we can only change a key that we own */ -	if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) { +	if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {  		key->perm = perm;  		ret = 0;  	} @@ -838,11 +927,11 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)  	key_put(key);  error:  	return ret; - -} /* end keyctl_setperm_key() */ +}  /* - * get the destination keyring for instantiation + * Get the destination keyring for instantiation and check that the caller has + * Write permission on it.   */  static long get_instantiation_keyring(key_serial_t ringid,  				      struct request_key_auth *rka, @@ -858,7 +947,7 @@ static long get_instantiation_keyring(key_serial_t ringid,  	/* if a specific keyring is nominated by ID, then use that */  	if (ringid > 0) { -		dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE); +		dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);  		if (IS_ERR(dkref))  			return PTR_ERR(dkref);  		*_dest_keyring = key_ref_to_ptr(dkref); @@ -879,7 +968,7 @@ static long get_instantiation_keyring(key_serial_t ringid,  }  /* - * change the request_key authorisation key on the current process + * Change the request_key authorisation key on the current process.   */  static int keyctl_change_reqkey_auth(struct key *key)  { @@ -895,15 +984,35 @@ static int keyctl_change_reqkey_auth(struct key *key)  	return commit_creds(new);  } -/*****************************************************************************/  /* - * instantiate the key with the specified payload, and, if one is given, link - * the key into the keyring + * Copy the iovec data from userspace   */ -long keyctl_instantiate_key(key_serial_t id, -			    const void __user *_payload, -			    size_t plen, -			    key_serial_t ringid) +static long copy_from_user_iovec(void *buffer, const struct iovec *iov, +				 unsigned ioc) +{ +	for (; ioc > 0; ioc--) { +		if (copy_from_user(buffer, iov->iov_base, iov->iov_len) != 0) +			return -EFAULT; +		buffer += iov->iov_len; +		iov++; +	} +	return 0; +} + +/* + * Instantiate a key with the specified payload and link the key into the + * destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority).  No other permissions are required. + * + * If successful, 0 will be returned. + */ +long keyctl_instantiate_key_common(key_serial_t id, +				   const struct iovec *payload_iov, +				   unsigned ioc, +				   size_t plen, +				   key_serial_t ringid)  {  	const struct cred *cred = current_cred();  	struct request_key_auth *rka; @@ -932,7 +1041,7 @@ long keyctl_instantiate_key(key_serial_t id,  	/* pull the payload in if one was supplied */  	payload = NULL; -	if (_payload) { +	if (payload_iov) {  		ret = -ENOMEM;  		payload = kmalloc(plen, GFP_KERNEL);  		if (!payload) { @@ -944,8 +1053,8 @@ long keyctl_instantiate_key(key_serial_t id,  				goto error;  		} -		ret = -EFAULT; -		if (copy_from_user(payload, _payload, plen) != 0) +		ret = copy_from_user_iovec(payload, payload_iov, ioc); +		if (ret < 0)  			goto error2;  	} @@ -973,22 +1082,127 @@ error2:  		vfree(payload);  error:  	return ret; +} + +/* + * Instantiate a key with the specified payload and link the key into the + * destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority).  No other permissions are required. + * + * If successful, 0 will be returned. + */ +long keyctl_instantiate_key(key_serial_t id, +			    const void __user *_payload, +			    size_t plen, +			    key_serial_t ringid) +{ +	if (_payload && plen) { +		struct iovec iov[1] = { +			[0].iov_base = (void __user *)_payload, +			[0].iov_len  = plen +		}; + +		return keyctl_instantiate_key_common(id, iov, 1, plen, ringid); +	} + +	return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid); +} + +/* + * Instantiate a key with the specified multipart payload and link the key into + * the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority).  No other permissions are required. + * + * If successful, 0 will be returned. + */ +long keyctl_instantiate_key_iov(key_serial_t id, +				const struct iovec __user *_payload_iov, +				unsigned ioc, +				key_serial_t ringid) +{ +	struct iovec iovstack[UIO_FASTIOV], *iov = iovstack; +	long ret; + +	if (!_payload_iov || !ioc) +		goto no_payload; + +	ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc, +				    ARRAY_SIZE(iovstack), iovstack, &iov); +	if (ret < 0) +		goto err; +	if (ret == 0) +		goto no_payload_free; + +	ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid); +err: +	if (iov != iovstack) +		kfree(iov); +	return ret; -} /* end keyctl_instantiate_key() */ +no_payload_free: +	if (iov != iovstack) +		kfree(iov); +no_payload: +	return keyctl_instantiate_key_common(id, NULL, 0, 0, ringid); +} -/*****************************************************************************/  /* - * negatively instantiate the key with the given timeout (in seconds), and, if - * one is given, link the key into the keyring + * Negatively instantiate the key with the given timeout (in seconds) and link + * the key into the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority).  No other permissions are required. + * + * The key and any links to the key will be automatically garbage collected + * after the timeout expires. + * + * Negative keys are used to rate limit repeated request_key() calls by causing + * them to return -ENOKEY until the negative key expires. + * + * If successful, 0 will be returned.   */  long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)  { +	return keyctl_reject_key(id, timeout, ENOKEY, ringid); +} + +/* + * Negatively instantiate the key with the given timeout (in seconds) and error + * code and link the key into the destination keyring if one is given. + * + * The caller must have the appropriate instantiation permit set for this to + * work (see keyctl_assume_authority).  No other permissions are required. + * + * The key and any links to the key will be automatically garbage collected + * after the timeout expires. + * + * Negative keys are used to rate limit repeated request_key() calls by causing + * them to return the specified error code until the negative key expires. + * + * If successful, 0 will be returned. + */ +long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, +		       key_serial_t ringid) +{  	const struct cred *cred = current_cred();  	struct request_key_auth *rka;  	struct key *instkey, *dest_keyring;  	long ret; -	kenter("%d,%u,%d", id, timeout, ringid); +	kenter("%d,%u,%u,%d", id, timeout, error, ringid); + +	/* must be a valid error code and mustn't be a kernel special */ +	if (error <= 0 || +	    error >= MAX_ERRNO || +	    error == ERESTARTSYS || +	    error == ERESTARTNOINTR || +	    error == ERESTARTNOHAND || +	    error == ERESTART_RESTARTBLOCK) +		return -EINVAL;  	/* the appropriate instantiation authorisation key must have been  	 * assumed before calling this */ @@ -1008,7 +1222,7 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)  		goto error;  	/* instantiate the key and link it into a keyring */ -	ret = key_negate_and_link(rka->target_key, timeout, +	ret = key_reject_and_link(rka->target_key, timeout, error,  				  dest_keyring, instkey);  	key_put(dest_keyring); @@ -1020,13 +1234,14 @@ long keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)  error:  	return ret; +} -} /* end keyctl_negate_key() */ - -/*****************************************************************************/  /* - * set the default keyring in which request_key() will cache keys - * - return the old setting + * Read or set the default keyring in which request_key() will cache keys and + * return the old setting. + * + * If a process keyring is specified then this will be created if it doesn't + * yet exist.  The old setting will be returned if successful.   */  long keyctl_set_reqkey_keyring(int reqkey_defl)  { @@ -1079,23 +1294,28 @@ set:  error:  	abort_creds(new);  	return ret; +} -} /* end keyctl_set_reqkey_keyring() */ - -/*****************************************************************************/  /* - * set or clear the timeout for a key + * Set or clear the timeout on a key. + * + * Either the key must grant the caller Setattr permission or else the caller + * must hold an instantiation authorisation token for the key. + * + * The timeout is either 0 to clear the timeout, or a number of seconds from + * the current time.  The key and any links to the key will be automatically + * garbage collected after the timeout expires. + * + * If successful, 0 is returned.   */  long keyctl_set_timeout(key_serial_t id, unsigned timeout)  { -	struct timespec now;  	struct key *key, *instkey;  	key_ref_t key_ref; -	time_t expiry;  	long ret;  	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, -				  KEY_SETATTR); +				  KEY_NEED_SETATTR);  	if (IS_ERR(key_ref)) {  		/* setting the timeout on a key under construction is permitted  		 * if we have the authorisation token handy */ @@ -1117,31 +1337,30 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)  okay:  	key = key_ref_to_ptr(key_ref); - -	/* make the changes with the locks held to prevent races */ -	down_write(&key->sem); - -	expiry = 0; -	if (timeout > 0) { -		now = current_kernel_time(); -		expiry = now.tv_sec + timeout; -	} - -	key->expiry = expiry; -	key_schedule_gc(key->expiry + key_gc_delay); - -	up_write(&key->sem); +	key_set_timeout(key, timeout);  	key_put(key);  	ret = 0;  error:  	return ret; +} -} /* end keyctl_set_timeout() */ - -/*****************************************************************************/  /* - * assume the authority to instantiate the specified key + * Assume (or clear) the authority to instantiate the specified key. + * + * This sets the authoritative token currently in force for key instantiation. + * This must be done for a key to be instantiated.  It has the effect of making + * available all the keys from the caller of the request_key() that created a + * key to request_key() calls made by the caller of this function. + * + * The caller must have the instantiation key in their process keyrings with a + * Search permission grant available to the caller. + * + * If the ID given is 0, then the setting will be cleared and 0 returned. + * + * If the ID given has a matching an authorisation key, then that key will be + * set and its ID will be returned.  The authorisation key can be read to get + * the callout information passed to request_key().   */  long keyctl_assume_authority(key_serial_t id)  { @@ -1178,16 +1397,17 @@ long keyctl_assume_authority(key_serial_t id)  	ret = authkey->serial;  error:  	return ret; - -} /* end keyctl_assume_authority() */ +}  /* - * get the security label of a key - * - the key must grant us view permission - * - if there's a buffer, we place up to buflen bytes of data into it - * - unless there's an error, we return the amount of information available, - *   irrespective of how much we may have copied (including the terminal NUL) - * - implements keyctl(KEYCTL_GET_SECURITY) + * Get a key's the LSM security label. + * + * The key must grant the caller View permission for this to work. + * + * If there's a buffer, then up to buflen bytes of data will be placed into it. + * + * If successful, the amount of information available will be returned, + * irrespective of how much was copied (including the terminal NUL).   */  long keyctl_get_security(key_serial_t keyid,  			 char __user *buffer, @@ -1198,7 +1418,7 @@ long keyctl_get_security(key_serial_t keyid,  	char *context;  	long ret; -	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW); +	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);  	if (IS_ERR(key_ref)) {  		if (PTR_ERR(key_ref) != -EACCES)  			return PTR_ERR(key_ref); @@ -1242,115 +1462,110 @@ long keyctl_get_security(key_serial_t keyid,  }  /* - * attempt to install the calling process's session keyring on the process's - * parent process - * - the keyring must exist and must grant us LINK permission - * - implements keyctl(KEYCTL_SESSION_TO_PARENT) + * Attempt to install the calling process's session keyring on the process's + * parent process. + * + * The keyring must exist and must grant the caller LINK permission, and the + * parent process must be single-threaded and must have the same effective + * ownership as this process and mustn't be SUID/SGID. + * + * The keyring will be emplaced on the parent when it next resumes userspace. + * + * If successful, 0 will be returned.   */  long keyctl_session_to_parent(void)  { -#ifdef TIF_NOTIFY_RESUME  	struct task_struct *me, *parent;  	const struct cred *mycred, *pcred; -	struct cred *cred, *oldcred; +	struct callback_head *newwork, *oldwork;  	key_ref_t keyring_r; +	struct cred *cred;  	int ret; -	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); +	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK);  	if (IS_ERR(keyring_r))  		return PTR_ERR(keyring_r); +	ret = -ENOMEM; +  	/* our parent is going to need a new cred struct, a new tgcred struct  	 * and new security data, so we allocate them here to prevent ENOMEM in  	 * our parent */ -	ret = -ENOMEM;  	cred = cred_alloc_blank();  	if (!cred)  		goto error_keyring; +	newwork = &cred->rcu; -	cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); +	cred->session_keyring = key_ref_to_ptr(keyring_r);  	keyring_r = NULL; +	init_task_work(newwork, key_change_session_keyring);  	me = current;  	rcu_read_lock();  	write_lock_irq(&tasklist_lock); -	parent = me->real_parent;  	ret = -EPERM; +	oldwork = NULL; +	parent = me->real_parent;  	/* the parent mustn't be init and mustn't be a kernel thread */  	if (parent->pid <= 1 || !parent->mm) -		goto not_permitted; +		goto unlock;  	/* the parent must be single threaded */  	if (!thread_group_empty(parent)) -		goto not_permitted; +		goto unlock;  	/* the parent and the child must have different session keyrings or  	 * there's no point */  	mycred = current_cred();  	pcred = __task_cred(parent);  	if (mycred == pcred || -	    mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) -		goto already_same; +	    mycred->session_keyring == pcred->session_keyring) { +		ret = 0; +		goto unlock; +	}  	/* the parent must have the same effective ownership and mustn't be  	 * SUID/SGID */ -	if (pcred->uid	!= mycred->euid	|| -	    pcred->euid	!= mycred->euid	|| -	    pcred->suid	!= mycred->euid	|| -	    pcred->gid	!= mycred->egid	|| -	    pcred->egid	!= mycred->egid	|| -	    pcred->sgid	!= mycred->egid) -		goto not_permitted; +	if (!uid_eq(pcred->uid,	 mycred->euid) || +	    !uid_eq(pcred->euid, mycred->euid) || +	    !uid_eq(pcred->suid, mycred->euid) || +	    !gid_eq(pcred->gid,	 mycred->egid) || +	    !gid_eq(pcred->egid, mycred->egid) || +	    !gid_eq(pcred->sgid, mycred->egid)) +		goto unlock;  	/* the keyrings must have the same UID */ -	if ((pcred->tgcred->session_keyring && -	     pcred->tgcred->session_keyring->uid != mycred->euid) || -	    mycred->tgcred->session_keyring->uid != mycred->euid) -		goto not_permitted; +	if ((pcred->session_keyring && +	     !uid_eq(pcred->session_keyring->uid, mycred->euid)) || +	    !uid_eq(mycred->session_keyring->uid, mycred->euid)) +		goto unlock; -	/* if there's an already pending keyring replacement, then we replace -	 * that */ -	oldcred = parent->replacement_session_keyring; +	/* cancel an already pending keyring replacement */ +	oldwork = task_work_cancel(parent, key_change_session_keyring);  	/* the replacement session keyring is applied just prior to userspace  	 * restarting */ -	parent->replacement_session_keyring = cred; -	cred = NULL; -	set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME); - -	write_unlock_irq(&tasklist_lock); -	rcu_read_unlock(); -	if (oldcred) -		put_cred(oldcred); -	return 0; - -already_same: -	ret = 0; -not_permitted: +	ret = task_work_add(parent, newwork, true); +	if (!ret) +		newwork = NULL; +unlock:  	write_unlock_irq(&tasklist_lock);  	rcu_read_unlock(); -	put_cred(cred); +	if (oldwork) +		put_cred(container_of(oldwork, struct cred, rcu)); +	if (newwork) +		put_cred(cred);  	return ret;  error_keyring:  	key_ref_put(keyring_r);  	return ret; - -#else /* !TIF_NOTIFY_RESUME */ -	/* -	 * To be removed when TIF_NOTIFY_RESUME has been implemented on -	 * m68k/xtensa -	 */ -#warning TIF_NOTIFY_RESUME not implemented -	return -EOPNOTSUPP; -#endif /* !TIF_NOTIFY_RESUME */  } -/*****************************************************************************/  /* - * the key control system call + * The key control system call   */  SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,  		unsigned long, arg4, unsigned long, arg5) @@ -1436,8 +1651,26 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,  	case KEYCTL_SESSION_TO_PARENT:  		return keyctl_session_to_parent(); +	case KEYCTL_REJECT: +		return keyctl_reject_key((key_serial_t) arg2, +					 (unsigned) arg3, +					 (unsigned) arg4, +					 (key_serial_t) arg5); + +	case KEYCTL_INSTANTIATE_IOV: +		return keyctl_instantiate_key_iov( +			(key_serial_t) arg2, +			(const struct iovec __user *) arg3, +			(unsigned) arg4, +			(key_serial_t) arg5); + +	case KEYCTL_INVALIDATE: +		return keyctl_invalidate_key((key_serial_t) arg2); + +	case KEYCTL_GET_PERSISTENT: +		return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3); +  	default:  		return -EOPNOTSUPP;  	} - -} /* end sys_keyctl() */ +} diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d37f713e73c..9cf2575f0d9 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1,6 +1,6 @@  /* Keyring handling   * - * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved. + * Copyright (C) 2004-2005, 2008, 2013 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com)   *   * This program is free software; you can redistribute it and/or @@ -17,25 +17,44 @@  #include <linux/seq_file.h>  #include <linux/err.h>  #include <keys/keyring-type.h> +#include <keys/user-type.h> +#include <linux/assoc_array_priv.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 + * 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 + * We keep all named keyrings in a hash to speed looking them up.   */  #define KEYRING_NAME_HASH_SIZE	(1 << 5) +/* + * We mark pointers we pass to the associative array with bit 1 set if + * they're keyrings and clear otherwise. + */ +#define KEYRING_PTR_SUBTYPE	0x2UL + +static inline bool keyring_ptr_is_keyring(const struct assoc_array_ptr *x) +{ +	return (unsigned long)x & KEYRING_PTR_SUBTYPE; +} +static inline struct key *keyring_ptr_to_key(const struct assoc_array_ptr *x) +{ +	void *object = assoc_array_ptr_to_leaf(x); +	return (struct key *)((unsigned long)object & ~KEYRING_PTR_SUBTYPE); +} +static inline void *keyring_key_to_ptr(struct key *key) +{ +	if (key->type == &key_type_keyring) +		return (void *)((unsigned long)key | KEYRING_PTR_SUBTYPE); +	return key; +} +  static struct list_head	keyring_name_hash[KEYRING_NAME_HASH_SIZE];  static DEFINE_RWLOCK(keyring_name_lock); @@ -50,11 +69,12 @@ static inline unsigned keyring_hash(const char *desc)  }  /* - * the keyring type definition + * 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); +			       struct key_preparsed_payload *prep);  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); @@ -63,27 +83,25 @@ static long keyring_read(const struct key *keyring,  struct key_type key_type_keyring = {  	.name		= "keyring", -	.def_datalen	= sizeof(struct keyring_list), +	.def_datalen	= 0,  	.instantiate	= keyring_instantiate, -	.match		= keyring_match, +	.match		= user_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 + * 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) + * 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)  { @@ -102,50 +120,259 @@ static void keyring_publish_name(struct key *keyring)  		write_unlock(&keyring_name_lock);  	} +} -} /* end keyring_publish_name() */ - -/*****************************************************************************/  /* - * initialise a keyring - * - we object if we were given any data + * 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) +			       struct key_preparsed_payload *prep)  {  	int ret;  	ret = -EINVAL; -	if (datalen == 0) { +	if (prep->datalen == 0) { +		assoc_array_init(&keyring->keys);  		/* make the keyring available by name if it has one */  		keyring_publish_name(keyring);  		ret = 0;  	}  	return ret; +} -} /* end keyring_instantiate() */ +/* + * Multiply 64-bits by 32-bits to 96-bits and fold back to 64-bit.  Ideally we'd + * fold the carry back too, but that requires inline asm. + */ +static u64 mult_64x32_and_fold(u64 x, u32 y) +{ +	u64 hi = (u64)(u32)(x >> 32) * y; +	u64 lo = (u64)(u32)(x) * y; +	return lo + ((u64)(u32)hi << 32) + (u32)(hi >> 32); +} -/*****************************************************************************/  /* - * match keyrings on their name + * Hash a key type and description.   */ -static int keyring_match(const struct key *keyring, const void *description) +static unsigned long hash_key_type_and_desc(const struct keyring_index_key *index_key)  { -	return keyring->description && -		strcmp(keyring->description, description) == 0; +	const unsigned level_shift = ASSOC_ARRAY_LEVEL_STEP; +	const unsigned long fan_mask = ASSOC_ARRAY_FAN_MASK; +	const char *description = index_key->description; +	unsigned long hash, type; +	u32 piece; +	u64 acc; +	int n, desc_len = index_key->desc_len; + +	type = (unsigned long)index_key->type; + +	acc = mult_64x32_and_fold(type, desc_len + 13); +	acc = mult_64x32_and_fold(acc, 9207); +	for (;;) { +		n = desc_len; +		if (n <= 0) +			break; +		if (n > 4) +			n = 4; +		piece = 0; +		memcpy(&piece, description, n); +		description += n; +		desc_len -= n; +		acc = mult_64x32_and_fold(acc, piece); +		acc = mult_64x32_and_fold(acc, 9207); +	} -} /* end keyring_match() */ +	/* Fold the hash down to 32 bits if need be. */ +	hash = acc; +	if (ASSOC_ARRAY_KEY_CHUNK_SIZE == 32) +		hash ^= acc >> 32; + +	/* Squidge all the keyrings into a separate part of the tree to +	 * ordinary keys by making sure the lowest level segment in the hash is +	 * zero for keyrings and non-zero otherwise. +	 */ +	if (index_key->type != &key_type_keyring && (hash & fan_mask) == 0) +		return hash | (hash >> (ASSOC_ARRAY_KEY_CHUNK_SIZE - level_shift)) | 1; +	if (index_key->type == &key_type_keyring && (hash & fan_mask) != 0) +		return (hash + (hash << level_shift)) & ~fan_mask; +	return hash; +} -/*****************************************************************************/  /* - * dispose of the data dangling from the corpse of a keyring + * Build the next index key chunk. + * + * On 32-bit systems the index key is laid out as: + * + *	0	4	5	9... + *	hash	desclen	typeptr	desc[] + * + * On 64-bit systems: + * + *	0	8	9	17... + *	hash	desclen	typeptr	desc[] + * + * We return it one word-sized chunk at a time.   */ -static void keyring_destroy(struct key *keyring) +static unsigned long keyring_get_key_chunk(const void *data, int level) +{ +	const struct keyring_index_key *index_key = data; +	unsigned long chunk = 0; +	long offset = 0; +	int desc_len = index_key->desc_len, n = sizeof(chunk); + +	level /= ASSOC_ARRAY_KEY_CHUNK_SIZE; +	switch (level) { +	case 0: +		return hash_key_type_and_desc(index_key); +	case 1: +		return ((unsigned long)index_key->type << 8) | desc_len; +	case 2: +		if (desc_len == 0) +			return (u8)((unsigned long)index_key->type >> +				    (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8)); +		n--; +		offset = 1; +	default: +		offset += sizeof(chunk) - 1; +		offset += (level - 3) * sizeof(chunk); +		if (offset >= desc_len) +			return 0; +		desc_len -= offset; +		if (desc_len > n) +			desc_len = n; +		offset += desc_len; +		do { +			chunk <<= 8; +			chunk |= ((u8*)index_key->description)[--offset]; +		} while (--desc_len > 0); + +		if (level == 2) { +			chunk <<= 8; +			chunk |= (u8)((unsigned long)index_key->type >> +				      (ASSOC_ARRAY_KEY_CHUNK_SIZE - 8)); +		} +		return chunk; +	} +} + +static unsigned long keyring_get_object_key_chunk(const void *object, int level) +{ +	const struct key *key = keyring_ptr_to_key(object); +	return keyring_get_key_chunk(&key->index_key, level); +} + +static bool keyring_compare_object(const void *object, const void *data) +{ +	const struct keyring_index_key *index_key = data; +	const struct key *key = keyring_ptr_to_key(object); + +	return key->index_key.type == index_key->type && +		key->index_key.desc_len == index_key->desc_len && +		memcmp(key->index_key.description, index_key->description, +		       index_key->desc_len) == 0; +} + +/* + * Compare the index keys of a pair of objects and determine the bit position + * at which they differ - if they differ. + */ +static int keyring_diff_objects(const void *object, const void *data) +{ +	const struct key *key_a = keyring_ptr_to_key(object); +	const struct keyring_index_key *a = &key_a->index_key; +	const struct keyring_index_key *b = data; +	unsigned long seg_a, seg_b; +	int level, i; + +	level = 0; +	seg_a = hash_key_type_and_desc(a); +	seg_b = hash_key_type_and_desc(b); +	if ((seg_a ^ seg_b) != 0) +		goto differ; + +	/* The number of bits contributed by the hash is controlled by a +	 * constant in the assoc_array headers.  Everything else thereafter we +	 * can deal with as being machine word-size dependent. +	 */ +	level += ASSOC_ARRAY_KEY_CHUNK_SIZE / 8; +	seg_a = a->desc_len; +	seg_b = b->desc_len; +	if ((seg_a ^ seg_b) != 0) +		goto differ; + +	/* The next bit may not work on big endian */ +	level++; +	seg_a = (unsigned long)a->type; +	seg_b = (unsigned long)b->type; +	if ((seg_a ^ seg_b) != 0) +		goto differ; + +	level += sizeof(unsigned long); +	if (a->desc_len == 0) +		goto same; + +	i = 0; +	if (((unsigned long)a->description | (unsigned long)b->description) & +	    (sizeof(unsigned long) - 1)) { +		do { +			seg_a = *(unsigned long *)(a->description + i); +			seg_b = *(unsigned long *)(b->description + i); +			if ((seg_a ^ seg_b) != 0) +				goto differ_plus_i; +			i += sizeof(unsigned long); +		} while (i < (a->desc_len & (sizeof(unsigned long) - 1))); +	} + +	for (; i < a->desc_len; i++) { +		seg_a = *(unsigned char *)(a->description + i); +		seg_b = *(unsigned char *)(b->description + i); +		if ((seg_a ^ seg_b) != 0) +			goto differ_plus_i; +	} + +same: +	return -1; + +differ_plus_i: +	level += i; +differ: +	i = level * 8 + __ffs(seg_a ^ seg_b); +	return i; +} + +/* + * Free an object after stripping the keyring flag off of the pointer. + */ +static void keyring_free_object(void *object)  { -	struct keyring_list *klist; -	int loop; +	key_put(keyring_ptr_to_key(object)); +} + +/* + * Operations for keyring management by the index-tree routines. + */ +static const struct assoc_array_ops keyring_assoc_array_ops = { +	.get_key_chunk		= keyring_get_key_chunk, +	.get_object_key_chunk	= keyring_get_object_key_chunk, +	.compare_object		= keyring_compare_object, +	.diff_objects		= keyring_diff_objects, +	.free_object		= keyring_free_object, +}; +/* + * 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) +{  	if (keyring->description) {  		write_lock(&keyring_name_lock); @@ -156,110 +383,110 @@ static void keyring_destroy(struct key *keyring)  		write_unlock(&keyring_name_lock);  	} -	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]); -		kfree(klist); -	} - -} /* end keyring_destroy() */ +	assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops); +} -/*****************************************************************************/  /* - * describe the keyring + * 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]"); -	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(); +	if (key_is_instantiated(keyring)) { +		if (keyring->keys.nr_leaves_on_tree != 0) +			seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree); +		else +			seq_puts(m, ": empty"); +	} +} + +struct keyring_read_iterator_context { +	size_t			qty; +	size_t			count; +	key_serial_t __user	*buffer; +}; -} /* end keyring_describe() */ +static int keyring_read_iterator(const void *object, void *data) +{ +	struct keyring_read_iterator_context *ctx = data; +	const struct key *key = keyring_ptr_to_key(object); +	int ret; + +	kenter("{%s,%d},,{%zu/%zu}", +	       key->type->name, key->serial, ctx->count, ctx->qty); + +	if (ctx->count >= ctx->qty) +		return 1; + +	ret = put_user(key->serial, ctx->buffer); +	if (ret < 0) +		return ret; +	ctx->buffer++; +	ctx->count += sizeof(key->serial); +	return 0; +} -/*****************************************************************************/  /* - * read a list of key IDs from the keyring's contents - * - the keyring's semaphore is read-locked + * Read a list of key IDs from the keyring's contents in binary form + * + * The keyring's semaphore is read-locked by the caller.  This prevents someone + * from modifying it under us - which could cause us to read key IDs multiple + * times.   */  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; +	struct keyring_read_iterator_context ctx; +	unsigned long nr_keys; +	int 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 = 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; -			} -		} +	kenter("{%d},,%zu", key_serial(keyring), buflen); -		ret = qty; -	} +	if (buflen & (sizeof(key_serial_t) - 1)) +		return -EINVAL; -error: -	return ret; +	nr_keys = keyring->keys.nr_leaves_on_tree; +	if (nr_keys == 0) +		return 0; -} /* end keyring_read() */ +	/* Calculate how much data we could return */ +	ctx.qty = nr_keys * sizeof(key_serial_t); + +	if (!buffer || !buflen) +		return ctx.qty; + +	if (buflen > ctx.qty) +		ctx.qty = buflen; + +	/* Copy the IDs of the subscribed keys into the buffer */ +	ctx.buffer = (key_serial_t __user *)buffer; +	ctx.count = 0; +	ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx); +	if (ret < 0) { +		kleave(" = %d [iterate]", ret); +		return ret; +	} + +	kleave(" = %zu [ok]", ctx.count); +	return ctx.count; +} -/*****************************************************************************/  /* - * allocate a keyring and link into the destination keyring + * 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_alloc(const char *description, kuid_t uid, kgid_t gid, +			  const struct cred *cred, key_perm_t perm, +			  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); - +			    uid, gid, cred, perm, flags);  	if (!IS_ERR(keyring)) {  		ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);  		if (ret < 0) { @@ -269,259 +496,452 @@ struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,  	}  	return keyring; +} +EXPORT_SYMBOL(keyring_alloc); -} /* 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 + * Iteration function to consider each key found.   */ -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) +static int keyring_search_iterator(const void *object, void *iterator_data)  { -	struct { -		struct keyring_list *keylist; -		int kix; -	} stack[KEYRING_SEARCH_MAX_DEPTH]; +	struct keyring_search_context *ctx = iterator_data; +	const struct key *key = keyring_ptr_to_key(object); +	unsigned long kflags = key->flags; -	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); +	kenter("{%d}", key->serial); -	/* 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; +	/* ignore keys not of this type */ +	if (key->type != ctx->index_key.type) { +		kleave(" = 0 [!type]"); +		return 0;  	} -	key_ref = ERR_PTR(-ENOTDIR); -	if (keyring->type != &key_type_keyring) -		goto error; +	/* skip invalidated, revoked and expired keys */ +	if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { +		if (kflags & ((1 << KEY_FLAG_INVALIDATED) | +			      (1 << KEY_FLAG_REVOKED))) { +			ctx->result = ERR_PTR(-EKEYREVOKED); +			kleave(" = %d [invrev]", ctx->skipped_ret); +			goto skipped; +		} -	rcu_read_lock(); +		if (key->expiry && ctx->now.tv_sec >= key->expiry) { +			ctx->result = ERR_PTR(-EKEYEXPIRED); +			kleave(" = %d [expire]", ctx->skipped_ret); +			goto skipped; +		} +	} -	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; +	/* keys that don't match */ +	if (!ctx->match(key, ctx->match_data)) { +		kleave(" = 0 [!match]"); +		return 0;  	} -	/* 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; +	/* key must have search permissions */ +	if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && +	    key_task_permission(make_key_ref(key, ctx->possessed), +				ctx->cred, KEY_NEED_SEARCH) < 0) { +		ctx->result = ERR_PTR(-EACCES); +		kleave(" = %d [!perm]", ctx->skipped_ret); +		goto skipped; +	} -	/* start processing a new keyring */ -descend: -	if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) -		goto not_this_keyring; +	if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) { +		/* we set a different error code if we pass a negative key */ +		if (kflags & (1 << KEY_FLAG_NEGATIVE)) { +			smp_rmb(); +			ctx->result = ERR_PTR(key->type_data.reject_error); +			kleave(" = %d [neg]", ctx->skipped_ret); +			goto skipped; +		} +	} -	keylist = rcu_dereference(keyring->payload.subscriptions); -	if (!keylist) -		goto not_this_keyring; +	/* Found */ +	ctx->result = make_key_ref(key, ctx->possessed); +	kleave(" = 1 [found]"); +	return 1; -	/* iterate through the keys in this keyring first */ -	for (kix = 0; kix < keylist->nkeys; kix++) { -		key = keylist->keys[kix]; -		kflags = key->flags; +skipped: +	return ctx->skipped_ret; +} -		/* ignore keys not of this type */ -		if (key->type != type) -			continue; +/* + * Search inside a keyring for a key.  We can search by walking to it + * directly based on its index-key or we can iterate over the entire + * tree looking for it, based on the match function. + */ +static int search_keyring(struct key *keyring, struct keyring_search_context *ctx) +{ +	if ((ctx->flags & KEYRING_SEARCH_LOOKUP_TYPE) == +	    KEYRING_SEARCH_LOOKUP_DIRECT) { +		const void *object; + +		object = assoc_array_find(&keyring->keys, +					  &keyring_assoc_array_ops, +					  &ctx->index_key); +		return object ? ctx->iterator(object, ctx) : 0; +	} +	return assoc_array_iterate(&keyring->keys, ctx->iterator, ctx); +} -		/* skip revoked keys and expired keys */ -		if (kflags & (1 << KEY_FLAG_REVOKED)) -			continue; +/* + * Search a tree of keyrings that point to other keyrings up to the maximum + * depth. + */ +static bool search_nested_keyrings(struct key *keyring, +				   struct keyring_search_context *ctx) +{ +	struct { +		struct key *keyring; +		struct assoc_array_node *node; +		int slot; +	} stack[KEYRING_SEARCH_MAX_DEPTH]; -		if (key->expiry && now.tv_sec >= key->expiry) -			continue; +	struct assoc_array_shortcut *shortcut; +	struct assoc_array_node *node; +	struct assoc_array_ptr *ptr; +	struct key *key; +	int sp = 0, slot; -		/* keys that don't match */ -		if (!match(key, description)) -			continue; +	kenter("{%d},{%s,%s}", +	       keyring->serial, +	       ctx->index_key.type->name, +	       ctx->index_key.description); -		/* key must have search permissions */ -		if (key_task_permission(make_key_ref(key, possessed), -					cred, KEY_SEARCH) < 0) -			continue; +	if (ctx->index_key.description) +		ctx->index_key.desc_len = strlen(ctx->index_key.description); -		/* we set a different error code if we pass a negative key */ -		if (kflags & (1 << KEY_FLAG_NEGATIVE)) { -			err = -ENOKEY; -			continue; +	/* Check to see if this top-level keyring is what we are looking for +	 * and whether it is valid or not. +	 */ +	if (ctx->flags & KEYRING_SEARCH_LOOKUP_ITERATE || +	    keyring_compare_object(keyring, &ctx->index_key)) { +		ctx->skipped_ret = 2; +		ctx->flags |= KEYRING_SEARCH_DO_STATE_CHECK; +		switch (ctx->iterator(keyring_key_to_ptr(keyring), ctx)) { +		case 1: +			goto found; +		case 2: +			return false; +		default: +			break;  		} +	} + +	ctx->skipped_ret = 0; +	if (ctx->flags & KEYRING_SEARCH_NO_STATE_CHECK) +		ctx->flags &= ~KEYRING_SEARCH_DO_STATE_CHECK; +	/* Start processing a new keyring */ +descend_to_keyring: +	kdebug("descend to %d", keyring->serial); +	if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | +			      (1 << KEY_FLAG_REVOKED))) +		goto not_this_keyring; + +	/* Search through the keys in this keyring before its searching its +	 * subtrees. +	 */ +	if (search_keyring(keyring, ctx))  		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; +	/* Then manually iterate through the keyrings nested in this one. +	 * +	 * Start from the root node of the index tree.  Because of the way the +	 * hash function has been set up, keyrings cluster on the leftmost +	 * branch of the root node (root slot 0) or in the root node itself. +	 * Non-keyrings avoid the leftmost branch of the root entirely (root +	 * slots 1-15). +	 */ +	ptr = ACCESS_ONCE(keyring->keys.root); +	if (!ptr) +		goto not_this_keyring; -		/* recursively search nested keyrings -		 * - only search keyrings for which we have search permission +	if (assoc_array_ptr_is_shortcut(ptr)) { +		/* If the root is a shortcut, either the keyring only contains +		 * keyring pointers (everything clusters behind root slot 0) or +		 * doesn't contain any keyring pointers.  		 */ -		if (sp >= KEYRING_SEARCH_MAX_DEPTH) +		shortcut = assoc_array_ptr_to_shortcut(ptr); +		smp_read_barrier_depends(); +		if ((shortcut->index_key[0] & ASSOC_ARRAY_FAN_MASK) != 0) +			goto not_this_keyring; + +		ptr = ACCESS_ONCE(shortcut->next_node); +		node = assoc_array_ptr_to_node(ptr); +		goto begin_node; +	} + +	node = assoc_array_ptr_to_node(ptr); +	smp_read_barrier_depends(); + +	ptr = node->slots[0]; +	if (!assoc_array_ptr_is_meta(ptr)) +		goto begin_node; + +descend_to_node: +	/* Descend to a more distal node in this keyring's content tree and go +	 * through that. +	 */ +	kdebug("descend"); +	if (assoc_array_ptr_is_shortcut(ptr)) { +		shortcut = assoc_array_ptr_to_shortcut(ptr); +		smp_read_barrier_depends(); +		ptr = ACCESS_ONCE(shortcut->next_node); +		BUG_ON(!assoc_array_ptr_is_node(ptr)); +	} +	node = assoc_array_ptr_to_node(ptr); + +begin_node: +	kdebug("begin_node"); +	smp_read_barrier_depends(); +	slot = 0; +ascend_to_node: +	/* Go through the slots in a node */ +	for (; slot < ASSOC_ARRAY_FAN_OUT; slot++) { +		ptr = ACCESS_ONCE(node->slots[slot]); + +		if (assoc_array_ptr_is_meta(ptr) && node->back_pointer) +			goto descend_to_node; + +		if (!keyring_ptr_is_keyring(ptr))  			continue; -		if (key_task_permission(make_key_ref(key, possessed), -					cred, KEY_SEARCH) < 0) +		key = keyring_ptr_to_key(ptr); + +		if (sp >= KEYRING_SEARCH_MAX_DEPTH) { +			if (ctx->flags & KEYRING_SEARCH_DETECT_TOO_DEEP) { +				ctx->result = ERR_PTR(-ELOOP); +				return false; +			} +			goto not_this_keyring; +		} + +		/* Search a nested keyring */ +		if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM) && +		    key_task_permission(make_key_ref(key, ctx->possessed), +					ctx->cred, KEY_NEED_SEARCH) < 0)  			continue;  		/* stack the current position */ -		stack[sp].keylist = keylist; -		stack[sp].kix = kix; +		stack[sp].keyring = keyring; +		stack[sp].node = node; +		stack[sp].slot = slot;  		sp++;  		/* begin again with the new keyring */  		keyring = key; -		goto descend; +		goto descend_to_keyring;  	} -	/* the keyring we're looking at was disqualified or didn't contain a -	 * matching key */ +	/* We've dealt with all the slots in the current node, so now we need +	 * to ascend to the parent and continue processing there. +	 */ +	ptr = ACCESS_ONCE(node->back_pointer); +	slot = node->parent_slot; + +	if (ptr && assoc_array_ptr_is_shortcut(ptr)) { +		shortcut = assoc_array_ptr_to_shortcut(ptr); +		smp_read_barrier_depends(); +		ptr = ACCESS_ONCE(shortcut->back_pointer); +		slot = shortcut->parent_slot; +	} +	if (!ptr) +		goto not_this_keyring; +	node = assoc_array_ptr_to_node(ptr); +	smp_read_barrier_depends(); +	slot++; + +	/* If we've ascended to the root (zero backpointer), we must have just +	 * finished processing the leftmost branch rather than the root slots - +	 * so there can't be any more keyrings for us to find. +	 */ +	if (node->back_pointer) { +		kdebug("ascend %d", slot); +		goto ascend_to_node; +	} + +	/* 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; +	kdebug("not_this_keyring %d", sp); +	if (sp <= 0) { +		kleave(" = false"); +		return false;  	} -	key_ref = ERR_PTR(err); -	goto error_2; +	/* Resume the processing of a keyring higher up in the tree */ +	sp--; +	keyring = stack[sp].keyring; +	node = stack[sp].node; +	slot = stack[sp].slot + 1; +	kdebug("ascend to %d [%d]", keyring->serial, slot); +	goto ascend_to_node; -	/* we found a viable match */ +	/* We found a viable match */  found: -	atomic_inc(&key->usage); +	key = key_ref_to_ptr(ctx->result);  	key_check(key); -	key_ref = make_key_ref(key, possessed); -error_2: -	rcu_read_unlock(); -error: -	return key_ref; +	if (!(ctx->flags & KEYRING_SEARCH_NO_UPDATE_TIME)) { +		key->last_used_at = ctx->now.tv_sec; +		keyring->last_used_at = ctx->now.tv_sec; +		while (sp > 0) +			stack[--sp].keyring->last_used_at = ctx->now.tv_sec; +	} +	kleave(" = true"); +	return true; +} + +/** + * keyring_search_aux - Search a keyring tree for a key matching some criteria + * @keyring_ref: A pointer to the keyring with possession indicator. + * @ctx: The keyring search context. + * + * 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 can be 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, +			     struct keyring_search_context *ctx) +{ +	struct key *keyring; +	long err; -} /* end keyring_search_aux() */ +	ctx->iterator = keyring_search_iterator; +	ctx->possessed = is_key_possessed(keyring_ref); +	ctx->result = ERR_PTR(-EAGAIN); -/*****************************************************************************/ -/* - * 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 +	keyring = key_ref_to_ptr(keyring_ref); +	key_check(keyring); + +	if (keyring->type != &key_type_keyring) +		return ERR_PTR(-ENOTDIR); + +	if (!(ctx->flags & KEYRING_SEARCH_NO_CHECK_PERM)) { +		err = key_task_permission(keyring_ref, ctx->cred, KEY_NEED_SEARCH); +		if (err < 0) +			return ERR_PTR(err); +	} + +	rcu_read_lock(); +	ctx->now = current_kernel_time(); +	if (search_nested_keyrings(keyring, ctx)) +		__key_get(key_ref_to_ptr(ctx->result)); +	rcu_read_unlock(); +	return ctx->result; +} + +/** + * keyring_search - Search the supplied keyring tree for a matching key + * @keyring: The root of the keyring tree to be searched. + * @type: The type of keyring we want to find. + * @description: The name of the keyring we want to find. + * + * As keyring_search_aux() above, but using the current task's credentials and + * type's default matching function and preferred search method.   */  key_ref_t keyring_search(key_ref_t keyring,  			 struct key_type *type,  			 const char *description)  { -	if (!type->match) +	struct keyring_search_context ctx = { +		.index_key.type		= type, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= type->match, +		.match_data		= description, +		.flags			= (type->def_lookup_type | +					   KEYRING_SEARCH_DO_STATE_CHECK), +	}; + +	if (!ctx.match)  		return ERR_PTR(-ENOKEY); -	return keyring_search_aux(keyring, current->cred, -				  type, description, type->match); - -} /* end keyring_search() */ - +	return keyring_search_aux(keyring, &ctx); +}  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 + * Search the given keyring for a key that might be updated. + * + * The caller must guarantee that the keyring is a keyring and that the + * permission is granted to modify the keyring as no check is made here.  The + * caller must also hold a lock on the keyring semaphore. + * + * Returns a pointer to the found key with usage count incremented if + * successful and returns NULL if not found.  Revoked and invalidated keys are + * skipped over. + * + * If successful, the possession indicator is propagated from the keyring ref + * to the returned key reference.   */ -key_ref_t __keyring_search_one(key_ref_t keyring_ref, -			       const struct key_type *ktype, -			       const char *description, -			       key_perm_t perm) +key_ref_t find_key_to_update(key_ref_t keyring_ref, +			     const struct keyring_index_key *index_key)  { -	struct keyring_list *klist; -	unsigned long possessed;  	struct key *keyring, *key; -	int loop; +	const void *object;  	keyring = key_ref_to_ptr(keyring_ref); -	possessed = is_key_possessed(keyring_ref); -	rcu_read_lock(); +	kenter("{%d},{%s,%s}", +	       keyring->serial, index_key->type->name, index_key->description); -	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; -		} -	} +	object = assoc_array_find(&keyring->keys, &keyring_assoc_array_ops, +				  index_key); -	rcu_read_unlock(); -	return ERR_PTR(-ENOKEY); +	if (object) +		goto found; -found: -	atomic_inc(&key->usage); -	rcu_read_unlock(); -	return make_key_ref(key, possessed); +	kleave(" = NULL"); +	return NULL; -} /* end __keyring_search_one() */ +found: +	key = keyring_ptr_to_key(object); +	if (key->flags & ((1 << KEY_FLAG_INVALIDATED) | +			  (1 << KEY_FLAG_REVOKED))) { +		kleave(" = NULL [x]"); +		return NULL; +	} +	__key_get(key); +	kleave(" = {%d}", key->serial); +	return make_key_ref(key, is_key_possessed(keyring_ref)); +} -/*****************************************************************************/  /* - * find a keyring with the specified name - * - all named keyrings are searched - * - normally only finds keyrings with search permission for the current process + * Find a keyring with the specified name. + * + * All named keyrings in the current user namespace are searched, provided they + * grant Search permission directly to the caller (unless this check is + * skipped).  Keyrings whose usage points have reached zero or who have been + * revoked are skipped. + * + * Returns a pointer to the keyring with the keyring's refcount having being + * incremented on success.  -ENOKEY is returned if a key could not be found.   */  struct key *find_keyring_by_name(const char *name, bool skip_perm_check)  { @@ -542,7 +962,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)  				    &keyring_name_hash[bucket],  				    type_data.link  				    ) { -			if (keyring->user->user_ns != current_user_ns()) +			if (!kuid_has_mapping(current_user_ns(), keyring->user->uid))  				continue;  			if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) @@ -553,7 +973,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)  			if (!skip_perm_check &&  			    key_permission(make_key_ref(keyring, 0), -					   KEY_SEARCH) < 0) +					   KEY_NEED_SEARCH) < 0)  				continue;  			/* we've got a match but we might end up racing with @@ -561,6 +981,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)  			 * (ie. it has a zero usage count) */  			if (!atomic_inc_not_zero(&keyring->usage))  				continue; +			keyring->last_used_at = current_kernel_time().tv_sec;  			goto out;  		}  	} @@ -569,125 +990,67 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check)  out:  	read_unlock(&keyring_name_lock);  	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) +static int keyring_detect_cycle_iterator(const void *object, +					 void *iterator_data)  { -	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 */ +	struct keyring_search_context *ctx = iterator_data; +	const struct key *key = keyring_ptr_to_key(object); -error: -	rcu_read_unlock(); -	return ret; +	kenter("{%d}", key->serial); -too_deep: -	ret = -ELOOP; -	goto error; +	/* We might get a keyring with matching index-key that is nonetheless a +	 * different keyring. */ +	if (key != ctx->match_data) +		return 0; -cycle_detected: -	ret = -EDEADLK; -	goto error; - -} /* end keyring_detect_cycle() */ +	ctx->result = ERR_PTR(-EDEADLK); +	return 1; +}  /* - * dispose of a keyring list after the RCU grace period, freeing the unlinked - * key + * 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 void keyring_unlink_rcu_disposal(struct rcu_head *rcu) +static int keyring_detect_cycle(struct key *A, struct key *B)  { -	struct keyring_list *klist = -		container_of(rcu, struct keyring_list, rcu); +	struct keyring_search_context ctx = { +		.index_key	= A->index_key, +		.match_data	= A, +		.iterator	= keyring_detect_cycle_iterator, +		.flags		= (KEYRING_SEARCH_LOOKUP_DIRECT | +				   KEYRING_SEARCH_NO_STATE_CHECK | +				   KEYRING_SEARCH_NO_UPDATE_TIME | +				   KEYRING_SEARCH_NO_CHECK_PERM | +				   KEYRING_SEARCH_DETECT_TOO_DEEP), +	}; -	if (klist->delkey != USHRT_MAX) -		key_put(klist->keys[klist->delkey]); -	kfree(klist); +	rcu_read_lock(); +	search_nested_keyrings(B, &ctx); +	rcu_read_unlock(); +	return PTR_ERR(ctx.result) == -EAGAIN ? 0 : PTR_ERR(ctx.result);  }  /* - * preallocate memory so that a key can be linked into to a keyring + * Preallocate memory so that a key can be linked into to a keyring.   */ -int __key_link_begin(struct key *keyring, const struct key_type *type, -		     const char *description, -		     struct keyring_list **_prealloc) +int __key_link_begin(struct key *keyring, +		     const struct keyring_index_key *index_key, +		     struct assoc_array_edit **_edit)  	__acquires(&keyring->sem) +	__acquires(&keyring_serialise_link_sem)  { -	struct keyring_list *klist, *nklist; -	unsigned max; -	size_t size; -	int loop, ret; +	struct assoc_array_edit *edit; +	int ret; + +	kenter("%d,%s,%s,", +	       keyring->serial, index_key->type->name, index_key->description); -	kenter("%d,%s,%s,", key_serial(keyring), type->name, description); +	BUG_ON(index_key->desc_len == 0);  	if (keyring->type != &key_type_keyring)  		return -ENOTDIR; @@ -700,90 +1063,39 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,  	/* serialise link/link calls to prevent parallel calls causing a cycle  	 * when linking two keyring in opposite orders */ -	if (type == &key_type_keyring) +	if (index_key->type == &key_type_keyring)  		down_write(&keyring_serialise_link_sem); -	klist = rcu_dereference_locked_keyring(keyring); - -	/* see if there's a matching key we can displace */ -	if (klist && klist->nkeys > 0) { -		for (loop = klist->nkeys - 1; loop >= 0; loop--) { -			if (klist->keys[loop]->type == type && -			    strcmp(klist->keys[loop]->description, -				   description) == 0 -			    ) { -				/* found a match - we'll replace this one with -				 * the 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 error_sem; - -				/* note replacement slot */ -				klist->delkey = nklist->delkey = loop; -				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) +	/* Create an edit script that will insert/replace the key in the +	 * keyring tree. +	 */ +	edit = assoc_array_insert(&keyring->keys, +				  &keyring_assoc_array_ops, +				  index_key, +				  NULL); +	if (IS_ERR(edit)) { +		ret = PTR_ERR(edit);  		goto error_sem; +	} -	if (klist && klist->nkeys < klist->maxkeys) { -		/* there's sufficient slack space to append directly */ -		nklist = NULL; -	} else { -		/* grow the key list */ -		max = 4; -		if (klist) -			max += klist->maxkeys; - -		ret = -ENFILE; -		if (max > USHRT_MAX - 1) -			goto error_quota; -		size = sizeof(*klist) + sizeof(struct key *) * max; -		if (size > PAGE_SIZE) -			goto error_quota; - -		ret = -ENOMEM; -		nklist = kmalloc(size, GFP_KERNEL); -		if (!nklist) -			goto error_quota; - -		nklist->maxkeys = max; -		if (klist) { -			memcpy(nklist->keys, klist->keys, -			       sizeof(struct key *) * klist->nkeys); -			nklist->delkey = klist->nkeys; -			nklist->nkeys = klist->nkeys + 1; -			klist->delkey = USHRT_MAX; -		} else { -			nklist->nkeys = 1; -			nklist->delkey = 0; -		} - -		/* add the key into the new space */ -		nklist->keys[nklist->delkey] = NULL; +	/* If we're not replacing a link in-place then we're going to need some +	 * extra quota. +	 */ +	if (!edit->dead_leaf) { +		ret = key_payload_reserve(keyring, +					  keyring->datalen + KEYQUOTA_LINK_BYTES); +		if (ret < 0) +			goto error_cancel;  	} -done: -	*_prealloc = nklist; +	*_edit = edit;  	kleave(" = 0");  	return 0; -error_quota: -	/* undo the quota changes */ -	key_payload_reserve(keyring, -			    keyring->datalen - KEYQUOTA_LINK_BYTES); +error_cancel: +	assoc_array_cancel_edit(edit);  error_sem: -	if (type == &key_type_keyring) +	if (index_key->type == &key_type_keyring)  		up_write(&keyring_serialise_link_sem);  error_krsem:  	up_write(&keyring->sem); @@ -792,10 +1104,10 @@ error_krsem:  }  /* - * check already instantiated keys aren't going to be a problem - * - the caller must have called __key_link_begin() - * - don't need to call this for keys that were created since __key_link_begin() - *   was called + * Check already instantiated keys aren't going to be a problem. + * + * The caller must have called __key_link_begin(). Don't need to call this for + * keys that were created since __key_link_begin() was called.   */  int __key_link_check_live_key(struct key *keyring, struct key *key)  { @@ -807,347 +1119,246 @@ int __key_link_check_live_key(struct key *keyring, struct key *key)  }  /* - * link a key into to a keyring - * - must be called with __key_link_begin() having being called - * - discard already extant link to matching key if there is one + * Link a key into to a keyring. + * + * Must be called with __key_link_begin() having being called.  Discards any + * already extant link to matching key if there is one, so that each keyring + * holds at most one link to any given key of a particular type+description + * combination.   */ -void __key_link(struct key *keyring, struct key *key, -		struct keyring_list **_prealloc) +void __key_link(struct key *key, struct assoc_array_edit **_edit)  { -	struct keyring_list *klist, *nklist; - -	nklist = *_prealloc; -	*_prealloc = NULL; - -	kenter("%d,%d,%p", keyring->serial, key->serial, nklist); - -	klist = rcu_dereference_protected(keyring->payload.subscriptions, -					  rwsem_is_locked(&keyring->sem)); - -	atomic_inc(&key->usage); - -	/* there's a matching key we can displace or an empty slot in a newly -	 * allocated list we can fill */ -	if (nklist) { -		kdebug("replace %hu/%hu/%hu", -		       nklist->delkey, nklist->nkeys, nklist->maxkeys); - -		nklist->keys[nklist->delkey] = key; - -		rcu_assign_pointer(keyring->payload.subscriptions, nklist); - -		/* dispose of the old keyring list and, if there was one, the -		 * displaced key */ -		if (klist) { -			kdebug("dispose %hu/%hu/%hu", -			       klist->delkey, klist->nkeys, klist->maxkeys); -			call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); -		} -	} else { -		/* there's sufficient slack space to append directly */ -		klist->keys[klist->nkeys] = key; -		smp_wmb(); -		klist->nkeys++; -	} +	__key_get(key); +	assoc_array_insert_set_object(*_edit, keyring_key_to_ptr(key)); +	assoc_array_apply_edit(*_edit); +	*_edit = NULL;  }  /* - * finish linking a key into to a keyring - * - must be called with __key_link_begin() having being called + * Finish linking a key into to a keyring. + * + * Must be called with __key_link_begin() having being called.   */ -void __key_link_end(struct key *keyring, struct key_type *type, -		    struct keyring_list *prealloc) +void __key_link_end(struct key *keyring, +		    const struct keyring_index_key *index_key, +		    struct assoc_array_edit *edit)  	__releases(&keyring->sem) +	__releases(&keyring_serialise_link_sem)  { -	BUG_ON(type == NULL); -	BUG_ON(type->name == NULL); -	kenter("%d,%s,%p", keyring->serial, type->name, prealloc); +	BUG_ON(index_key->type == NULL); +	kenter("%d,%s,", keyring->serial, index_key->type->name); -	if (type == &key_type_keyring) +	if (index_key->type == &key_type_keyring)  		up_write(&keyring_serialise_link_sem); -	if (prealloc) { -		kfree(prealloc); +	if (edit && !edit->dead_leaf) {  		key_payload_reserve(keyring,  				    keyring->datalen - KEYQUOTA_LINK_BYTES); +		assoc_array_cancel_edit(edit);  	}  	up_write(&keyring->sem);  } -/* - * link a key to a keyring +/** + * key_link - Link a key to a keyring + * @keyring: The keyring to make the link in. + * @key: The key to link to. + * + * Make a link in a keyring to a key, such that the keyring holds a reference + * on that key and the key can potentially be found by searching that keyring. + * + * This function will write-lock the keyring's semaphore and will consume some + * of the user's key data quota to hold the link. + * + * Returns 0 if successful, -ENOTDIR if the keyring isn't a keyring, + * -EKEYREVOKED if the keyring has been revoked, -ENFILE if the keyring is + * full, -EDQUOT if there is insufficient key data quota remaining to add + * another link or -ENOMEM if there's insufficient memory. + * + * It is assumed that the caller has checked that it is permitted for a link to + * be made (the keyring should have Write permission and the key Link + * permission).   */  int key_link(struct key *keyring, struct key *key)  { -	struct keyring_list *prealloc; +	struct assoc_array_edit *edit;  	int ret; +	kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage)); +  	key_check(keyring);  	key_check(key); -	ret = __key_link_begin(keyring, key->type, key->description, &prealloc); +	if (test_bit(KEY_FLAG_TRUSTED_ONLY, &keyring->flags) && +	    !test_bit(KEY_FLAG_TRUSTED, &key->flags)) +		return -EPERM; + +	ret = __key_link_begin(keyring, &key->index_key, &edit);  	if (ret == 0) { +		kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage));  		ret = __key_link_check_live_key(keyring, key);  		if (ret == 0) -			__key_link(keyring, key, &prealloc); -		__key_link_end(keyring, key->type, prealloc); +			__key_link(key, &edit); +		__key_link_end(keyring, &key->index_key, edit);  	} +	kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage));  	return ret;  } -  EXPORT_SYMBOL(key_link); -/*****************************************************************************/ -/* - * unlink the first link to a key from a keyring +/** + * key_unlink - Unlink the first link to a key from a keyring. + * @keyring: The keyring to remove the link from. + * @key: The key the link is to. + * + * Remove a link from a keyring to a key. + * + * This function will write-lock the keyring's semaphore. + * + * Returns 0 if successful, -ENOTDIR if the keyring isn't a keyring, -ENOENT if + * the key isn't linked to by the keyring or -ENOMEM if there's insufficient + * memory. + * + * It is assumed that the caller has checked that it is permitted for a link to + * be removed (the keyring should have Write permission; no permissions are + * required on the key).   */  int key_unlink(struct key *keyring, struct key *key)  { -	struct keyring_list *klist, *nklist; -	int loop, ret; +	struct assoc_array_edit *edit; +	int ret;  	key_check(keyring);  	key_check(key); -	ret = -ENOTDIR;  	if (keyring->type != &key_type_keyring) -		goto error; +		return -ENOTDIR;  	down_write(&keyring->sem); -	klist = rcu_dereference_locked_keyring(keyring); -	if (klist) { -		/* search the keyring for the key */ -		for (loop = 0; loop < klist->nkeys; loop++) -			if (klist->keys[loop] == key) -				goto key_is_present; +	edit = assoc_array_delete(&keyring->keys, &keyring_assoc_array_ops, +				  &key->index_key); +	if (IS_ERR(edit)) { +		ret = PTR_ERR(edit); +		goto error;  	} - -	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); +	if (edit == NULL) +		goto error; +	assoc_array_apply_edit(edit); +	key_payload_reserve(keyring, keyring->datalen - KEYQUOTA_LINK_BYTES);  	ret = 0;  error: -	return ret; -nomem: -	ret = -ENOMEM;  	up_write(&keyring->sem); -	goto error; - -} /* end key_unlink() */ - +	return ret; +}  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) +/** + * keyring_clear - Clear a keyring + * @keyring: The keyring to clear. + * + * Clear the contents of the specified keyring. + * + * Returns 0 if successful or -ENOTDIR if the keyring isn't a keyring.   */  int keyring_clear(struct key *keyring)  { -	struct keyring_list *klist; +	struct assoc_array_edit *edit;  	int ret; -	ret = -ENOTDIR; -	if (keyring->type == &key_type_keyring) { -		/* detach the pointer block with the locks held */ -		down_write(&keyring->sem); - -		klist = rcu_dereference_locked_keyring(keyring); -		if (klist) { -			/* adjust the quota */ -			key_payload_reserve(keyring, -					    sizeof(struct keyring_list)); - -			rcu_assign_pointer(keyring->payload.subscriptions, -					   NULL); -		} - -		up_write(&keyring->sem); +	if (keyring->type != &key_type_keyring) +		return -ENOTDIR; -		/* free the keys after the locks have been dropped */ -		if (klist) -			call_rcu(&klist->rcu, keyring_clear_rcu_disposal); +	down_write(&keyring->sem); +	edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops); +	if (IS_ERR(edit)) { +		ret = PTR_ERR(edit); +	} else { +		if (edit) +			assoc_array_apply_edit(edit); +		key_payload_reserve(keyring, 0);  		ret = 0;  	} +	up_write(&keyring->sem);  	return ret; - -} /* end keyring_clear() */ - +}  EXPORT_SYMBOL(keyring_clear); -/*****************************************************************************/  /* - * dispose of the links from a revoked keyring - * - called with the key sem write-locked + * Dispose of the links from a revoked keyring. + * + * This is called with the key sem write-locked.   */  static void keyring_revoke(struct key *keyring)  { -	struct keyring_list *klist; +	struct assoc_array_edit *edit; -	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); +	edit = assoc_array_clear(&keyring->keys, &keyring_assoc_array_ops); +	if (!IS_ERR(edit)) { +		if (edit) +			assoc_array_apply_edit(edit); +		key_payload_reserve(keyring, 0);  	} +} -} /* end keyring_revoke() */ +static bool keyring_gc_select_iterator(void *object, void *iterator_data) +{ +	struct key *key = keyring_ptr_to_key(object); +	time_t *limit = iterator_data; -/* - * Determine whether a key is dead - */ -static bool key_is_dead(struct key *key, time_t limit) +	if (key_is_dead(key, *limit)) +		return false; +	key_get(key); +	return true; +} + +static int keyring_gc_check_iterator(const void *object, void *iterator_data)  { -	return test_bit(KEY_FLAG_DEAD, &key->flags) || -		(key->expiry > 0 && key->expiry <= limit); +	const struct key *key = keyring_ptr_to_key(object); +	time_t *limit = iterator_data; + +	key_check(key); +	return key_is_dead(key, *limit);  }  /* - * Collect garbage from the contents of a keyring + * Garbage collect pointers from a keyring. + * + * Not called with any locks held.  The keyring's key struct will not be + * deallocated under us as only our caller may deallocate it.   */  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); +	int result; -	if (keep == 0) { -		rcu_assign_pointer(keyring->payload.subscriptions, NULL); -		kfree(new); -	} else { -		rcu_assign_pointer(keyring->payload.subscriptions, new); -	} +	kenter("%x{%s}", keyring->serial, keyring->description ?: ""); -	up_write(&keyring->sem); +	if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | +			      (1 << KEY_FLAG_REVOKED))) +		goto dont_gc; -	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; +	/* scan the keyring looking for dead keys */ +	rcu_read_lock(); +	result = assoc_array_iterate(&keyring->keys, +				     keyring_gc_check_iterator, &limit); +	rcu_read_unlock(); +	if (result == true) +		goto do_gc; -no_klist: -	up_write(&keyring->sem); -	kleave(" [no_klist]"); +dont_gc: +	kleave(" [no gc]");  	return; -nomem: +do_gc: +	down_write(&keyring->sem); +	assoc_array_gc(&keyring->keys, &keyring_assoc_array_ops, +		       keyring_gc_select_iterator, &limit);  	up_write(&keyring->sem); -	kleave(" [oom]"); +	kleave(" [gc]");  } diff --git a/security/keys/permission.c b/security/keys/permission.c index 28645502cd0..732cc0beffd 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -1,4 +1,4 @@ -/* permission.c: key permission determination +/* Key permission checking   *   * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -13,21 +13,22 @@  #include <linux/security.h>  #include "internal.h" -/*****************************************************************************/  /**   * key_task_permission - Check a key can be used - * @key_ref: The key to check - * @cred: The credentials to use - * @perm: The permissions to check for + * @key_ref: The key to check. + * @cred: The credentials to use. + * @perm: The permissions to check for.   *   * Check to see whether permission is granted to use a key in the desired way,   * but permit the security modules to override.   * - * The caller must hold either a ref on cred or must hold the RCU readlock or a - * spinlock. + * The caller must hold either a ref on cred or must hold the RCU readlock. + * + * Returns 0 if successful, -EACCES if access is denied based on the + * permissions bits or the LSM check.   */  int key_task_permission(const key_ref_t key_ref, const struct cred *cred, -			key_perm_t perm) +			unsigned perm)  {  	struct key *key;  	key_perm_t kperm; @@ -35,19 +36,16 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,  	key = key_ref_to_ptr(key_ref); -	if (key->user->user_ns != cred->user->user_ns) -		goto use_other_perms; -  	/* use the second 8-bits of permissions for keys the caller owns */ -	if (key->uid == cred->fsuid) { +	if (uid_eq(key->uid, cred->fsuid)) {  		kperm = key->perm >> 16;  		goto use_these_perms;  	}  	/* use the third 8-bits of permissions for keys the caller has a group  	 * membership in common with */ -	if (key->gid != -1 && key->perm & KEY_GRP_ALL) { -		if (key->gid == cred->fsgid) { +	if (gid_valid(key->gid) && key->perm & KEY_GRP_ALL) { +		if (gid_eq(key->gid, cred->fsgid)) {  			kperm = key->perm >> 8;  			goto use_these_perms;  		} @@ -59,8 +57,6 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred,  		}  	} -use_other_perms: -  	/* otherwise use the least-significant 8-bits */  	kperm = key->perm; @@ -72,46 +68,43 @@ use_these_perms:  	if (is_key_possessed(key_ref))  		kperm |= key->perm >> 24; -	kperm = kperm & perm & KEY_ALL; +	kperm = kperm & perm & KEY_NEED_ALL;  	if (kperm != perm)  		return -EACCES;  	/* let LSM be the final arbiter */  	return security_key_permission(key_ref, cred, perm); - -} /* end key_task_permission() */ - +}  EXPORT_SYMBOL(key_task_permission); -/*****************************************************************************/ -/* - * validate a key +/** + * key_validate - Validate a key. + * @key: The key to be validated. + * + * Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the + * key is invalidated, -EKEYREVOKED if the key's type has been removed or if + * the key has been revoked or -EKEYEXPIRED if the key has expired.   */ -int key_validate(struct key *key) +int key_validate(const struct key *key)  { -	struct timespec now; -	int ret = 0; - -	if (key) { -		/* check it's still accessible */ -		ret = -EKEYREVOKED; -		if (test_bit(KEY_FLAG_REVOKED, &key->flags) || -		    test_bit(KEY_FLAG_DEAD, &key->flags)) -			goto error; - -		/* check it hasn't expired */ -		ret = 0; -		if (key->expiry) { -			now = current_kernel_time(); -			if (now.tv_sec >= key->expiry) -				ret = -EKEYEXPIRED; -		} -	} +	unsigned long flags = key->flags; -error: -	return ret; +	if (flags & (1 << KEY_FLAG_INVALIDATED)) +		return -ENOKEY; -} /* end key_validate() */ +	/* check it's still accessible */ +	if (flags & ((1 << KEY_FLAG_REVOKED) | +		     (1 << KEY_FLAG_DEAD))) +		return -EKEYREVOKED; + +	/* check it hasn't expired */ +	if (key->expiry) { +		struct timespec now = current_kernel_time(); +		if (now.tv_sec >= key->expiry) +			return -EKEYEXPIRED; +	} +	return 0; +}  EXPORT_SYMBOL(key_validate); diff --git a/security/keys/persistent.c b/security/keys/persistent.c new file mode 100644 index 00000000000..c9fae5ea89f --- /dev/null +++ b/security/keys/persistent.c @@ -0,0 +1,167 @@ +/* General persistent per-UID keyrings register + * + * Copyright (C) 2013 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 Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/user_namespace.h> +#include "internal.h" + +unsigned persistent_keyring_expiry = 3 * 24 * 3600; /* Expire after 3 days of non-use */ + +/* + * Create the persistent keyring register for the current user namespace. + * + * Called with the namespace's sem locked for writing. + */ +static int key_create_persistent_register(struct user_namespace *ns) +{ +	struct key *reg = keyring_alloc(".persistent_register", +					KUIDT_INIT(0), KGIDT_INIT(0), +					current_cred(), +					((KEY_POS_ALL & ~KEY_POS_SETATTR) | +					 KEY_USR_VIEW | KEY_USR_READ), +					KEY_ALLOC_NOT_IN_QUOTA, NULL); +	if (IS_ERR(reg)) +		return PTR_ERR(reg); + +	ns->persistent_keyring_register = reg; +	return 0; +} + +/* + * Create the persistent keyring for the specified user. + * + * Called with the namespace's sem locked for writing. + */ +static key_ref_t key_create_persistent(struct user_namespace *ns, kuid_t uid, +				       struct keyring_index_key *index_key) +{ +	struct key *persistent; +	key_ref_t reg_ref, persistent_ref; + +	if (!ns->persistent_keyring_register) { +		long err = key_create_persistent_register(ns); +		if (err < 0) +			return ERR_PTR(err); +	} else { +		reg_ref = make_key_ref(ns->persistent_keyring_register, true); +		persistent_ref = find_key_to_update(reg_ref, index_key); +		if (persistent_ref) +			return persistent_ref; +	} + +	persistent = keyring_alloc(index_key->description, +				   uid, INVALID_GID, current_cred(), +				   ((KEY_POS_ALL & ~KEY_POS_SETATTR) | +				    KEY_USR_VIEW | KEY_USR_READ), +				   KEY_ALLOC_NOT_IN_QUOTA, +				   ns->persistent_keyring_register); +	if (IS_ERR(persistent)) +		return ERR_CAST(persistent); + +	return make_key_ref(persistent, true); +} + +/* + * Get the persistent keyring for a specific UID and link it to the nominated + * keyring. + */ +static long key_get_persistent(struct user_namespace *ns, kuid_t uid, +			       key_ref_t dest_ref) +{ +	struct keyring_index_key index_key; +	struct key *persistent; +	key_ref_t reg_ref, persistent_ref; +	char buf[32]; +	long ret; + +	/* Look in the register if it exists */ +	index_key.type = &key_type_keyring; +	index_key.description = buf; +	index_key.desc_len = sprintf(buf, "_persistent.%u", from_kuid(ns, uid)); + +	if (ns->persistent_keyring_register) { +		reg_ref = make_key_ref(ns->persistent_keyring_register, true); +		down_read(&ns->persistent_keyring_register_sem); +		persistent_ref = find_key_to_update(reg_ref, &index_key); +		up_read(&ns->persistent_keyring_register_sem); + +		if (persistent_ref) +			goto found; +	} + +	/* It wasn't in the register, so we'll need to create it.  We might +	 * also need to create the register. +	 */ +	down_write(&ns->persistent_keyring_register_sem); +	persistent_ref = key_create_persistent(ns, uid, &index_key); +	up_write(&ns->persistent_keyring_register_sem); +	if (!IS_ERR(persistent_ref)) +		goto found; + +	return PTR_ERR(persistent_ref); + +found: +	ret = key_task_permission(persistent_ref, current_cred(), KEY_NEED_LINK); +	if (ret == 0) { +		persistent = key_ref_to_ptr(persistent_ref); +		ret = key_link(key_ref_to_ptr(dest_ref), persistent); +		if (ret == 0) { +			key_set_timeout(persistent, persistent_keyring_expiry); +			ret = persistent->serial;		 +		} +	} + +	key_ref_put(persistent_ref); +	return ret; +} + +/* + * Get the persistent keyring for a specific UID and link it to the nominated + * keyring. + */ +long keyctl_get_persistent(uid_t _uid, key_serial_t destid) +{ +	struct user_namespace *ns = current_user_ns(); +	key_ref_t dest_ref; +	kuid_t uid; +	long ret; + +	/* -1 indicates the current user */ +	if (_uid == (uid_t)-1) { +		uid = current_uid(); +	} else { +		uid = make_kuid(ns, _uid); +		if (!uid_valid(uid)) +			return -EINVAL; + +		/* You can only see your own persistent cache if you're not +		 * sufficiently privileged. +		 */ +		if (!uid_eq(uid, current_uid()) && +		    !uid_eq(uid, current_euid()) && +		    !ns_capable(ns, CAP_SETUID)) +			return -EPERM; +	} + +	/* There must be a destination keyring */ +	dest_ref = lookup_user_key(destid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE); +	if (IS_ERR(dest_ref)) +		return PTR_ERR(dest_ref); +	if (key_ref_to_ptr(dest_ref)->type != &key_type_keyring) { +		ret = -ENOTDIR; +		goto out_put_dest; +	} + +	ret = key_get_persistent(ns, uid, dest_ref); + +out_put_dest: +	key_ref_put(dest_ref); +	return ret; +} diff --git a/security/keys/proc.c b/security/keys/proc.c index 70373966816..d3f6f2fd21d 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -1,4 +1,4 @@ -/* proc.c: proc files for key database enumeration +/* procfs files for key database enumeration   *   * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -60,9 +60,8 @@ static const struct file_operations proc_key_users_fops = {  	.release	= seq_release,  }; -/*****************************************************************************/  /* - * declare the /proc files + * Declare the /proc files.   */  static int __init key_proc_init(void)  { @@ -79,25 +78,24 @@ static int __init key_proc_init(void)  		panic("Cannot create /proc/key-users\n");  	return 0; - -} /* end key_proc_init() */ +}  __initcall(key_proc_init); -/*****************************************************************************/  /* - * implement "/proc/keys" to provides a list of the keys on the system + * Implement "/proc/keys" to provide a list of the keys on the system that + * grant View permission to the caller.   */  #ifdef CONFIG_KEYS_DEBUG_PROC_KEYS -static struct rb_node *key_serial_next(struct rb_node *n) +static struct rb_node *key_serial_next(struct seq_file *p, struct rb_node *n)  { -	struct user_namespace *user_ns = current_user_ns(); +	struct user_namespace *user_ns = seq_user_ns(p);  	n = rb_next(n);  	while (n) {  		struct key *key = rb_entry(n, struct key, serial_node); -		if (key->user->user_ns == user_ns) +		if (kuid_has_mapping(user_ns, key->user->uid))  			break;  		n = rb_next(n);  	} @@ -109,9 +107,9 @@ static int proc_keys_open(struct inode *inode, struct file *file)  	return seq_open(file, &proc_keys_ops);  } -static struct key *find_ge_key(key_serial_t id) +static struct key *find_ge_key(struct seq_file *p, key_serial_t id)  { -	struct user_namespace *user_ns = current_user_ns(); +	struct user_namespace *user_ns = seq_user_ns(p);  	struct rb_node *n = key_serial_tree.rb_node;  	struct key *minkey = NULL; @@ -134,7 +132,7 @@ static struct key *find_ge_key(key_serial_t id)  		return NULL;  	for (;;) { -		if (minkey->user->user_ns == user_ns) +		if (kuid_has_mapping(user_ns, minkey->user->uid))  			return minkey;  		n = rb_next(&minkey->serial_node);  		if (!n) @@ -153,7 +151,7 @@ static void *proc_keys_start(struct seq_file *p, loff_t *_pos)  	if (*_pos > INT_MAX)  		return NULL; -	key = find_ge_key(pos); +	key = find_ge_key(p, pos);  	if (!key)  		return NULL;  	*_pos = key->serial; @@ -170,7 +168,7 @@ static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)  {  	struct rb_node *n; -	n = key_serial_next(v); +	n = key_serial_next(p, v);  	if (n)  		*_pos = key_node_serial(n);  	return n; @@ -184,7 +182,6 @@ static void proc_keys_stop(struct seq_file *p, void *v)  static int proc_keys_show(struct seq_file *m, void *v)  { -	const struct cred *cred = current_cred();  	struct rb_node *_p = v;  	struct key *key = rb_entry(_p, struct key, serial_node);  	struct timespec now; @@ -193,15 +190,23 @@ static int proc_keys_show(struct seq_file *m, void *v)  	char xbuf[12];  	int rc; +	struct keyring_search_context ctx = { +		.index_key.type		= key->type, +		.index_key.description	= key->description, +		.cred			= current_cred(), +		.match			= lookup_user_key_possessed, +		.match_data		= key, +		.flags			= (KEYRING_SEARCH_NO_STATE_CHECK | +					   KEYRING_SEARCH_LOOKUP_DIRECT), +	}; +  	key_ref = make_key_ref(key, 0);  	/* determine if the key is possessed by this process (a test we can  	 * skip if the key does not indicate the possessor can view it  	 */  	if (key->perm & KEY_POS_VIEW) { -		skey_ref = search_my_process_keyrings(key->type, key, -						      lookup_user_key_possessed, -						      cred); +		skey_ref = search_my_process_keyrings(&ctx);  		if (!IS_ERR(skey_ref)) {  			key_ref_put(skey_ref);  			key_ref = make_key_ref(key, 1); @@ -213,7 +218,7 @@ static int proc_keys_show(struct seq_file *m, void *v)  	 * - the caller holds a spinlock, and thus the RCU read lock, making our  	 *   access to __current_cred() safe  	 */ -	rc = key_task_permission(key_ref, cred, KEY_VIEW); +	rc = key_task_permission(key_ref, ctx.cred, KEY_NEED_VIEW);  	if (rc < 0)  		return 0; @@ -244,7 +249,7 @@ static int proc_keys_show(struct seq_file *m, void *v)  #define showflag(KEY, LETTER, FLAG) \  	(test_bit(FLAG,	&(KEY)->flags) ? LETTER : '-') -	seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", +	seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",  		   key->serial,  		   showflag(key, 'I', KEY_FLAG_INSTANTIATED),  		   showflag(key, 'R', KEY_FLAG_REVOKED), @@ -252,11 +257,12 @@ static int proc_keys_show(struct seq_file *m, void *v)  		   showflag(key, 'Q', KEY_FLAG_IN_QUOTA),  		   showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),  		   showflag(key, 'N', KEY_FLAG_NEGATIVE), +		   showflag(key, 'i', KEY_FLAG_INVALIDATED),  		   atomic_read(&key->usage),  		   xbuf,  		   key->perm, -		   key->uid, -		   key->gid, +		   from_kuid_munged(seq_user_ns(m), key->uid), +		   from_kgid_munged(seq_user_ns(m), key->gid),  		   key->type->name);  #undef showflag @@ -271,31 +277,31 @@ static int proc_keys_show(struct seq_file *m, void *v)  #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ -static struct rb_node *__key_user_next(struct rb_node *n) +static struct rb_node *__key_user_next(struct user_namespace *user_ns, struct rb_node *n)  {  	while (n) {  		struct key_user *user = rb_entry(n, struct key_user, node); -		if (user->user_ns == current_user_ns()) +		if (kuid_has_mapping(user_ns, user->uid))  			break;  		n = rb_next(n);  	}  	return n;  } -static struct rb_node *key_user_next(struct rb_node *n) +static struct rb_node *key_user_next(struct user_namespace *user_ns, struct rb_node *n)  { -	return __key_user_next(rb_next(n)); +	return __key_user_next(user_ns, rb_next(n));  } -static struct rb_node *key_user_first(struct rb_root *r) +static struct rb_node *key_user_first(struct user_namespace *user_ns, struct rb_root *r)  {  	struct rb_node *n = rb_first(r); -	return __key_user_next(n); +	return __key_user_next(user_ns, n);  } -/*****************************************************************************/  /* - * implement "/proc/key-users" to provides a list of the key users + * Implement "/proc/key-users" to provides a list of the key users and their + * quotas.   */  static int proc_key_users_open(struct inode *inode, struct file *file)  { @@ -310,10 +316,10 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)  	spin_lock(&key_user_lock); -	_p = key_user_first(&key_user_tree); +	_p = key_user_first(seq_user_ns(p), &key_user_tree);  	while (pos > 0 && _p) {  		pos--; -		_p = key_user_next(_p); +		_p = key_user_next(seq_user_ns(p), _p);  	}  	return _p; @@ -322,7 +328,7 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)  static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)  {  	(*_pos)++; -	return key_user_next((struct rb_node *)v); +	return key_user_next(seq_user_ns(p), (struct rb_node *)v);  }  static void proc_key_users_stop(struct seq_file *p, void *v) @@ -335,13 +341,13 @@ static int proc_key_users_show(struct seq_file *m, void *v)  {  	struct rb_node *_p = v;  	struct key_user *user = rb_entry(_p, struct key_user, node); -	unsigned maxkeys = (user->uid == 0) ? +	unsigned maxkeys = uid_eq(user->uid, GLOBAL_ROOT_UID) ?  		key_quota_root_maxkeys : key_quota_maxkeys; -	unsigned maxbytes = (user->uid == 0) ? +	unsigned maxbytes = uid_eq(user->uid, GLOBAL_ROOT_UID) ?  		key_quota_root_maxbytes : key_quota_maxbytes;  	seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", -		   user->uid, +		   from_kuid_munged(seq_user_ns(m), user->uid),  		   atomic_read(&user->usage),  		   atomic_read(&user->nkeys),  		   atomic_read(&user->nikeys), @@ -351,5 +357,4 @@ static int proc_key_users_show(struct seq_file *m, void *v)  		   maxbytes);  	return 0; -  } diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 504bdd2452b..0cf8a130a26 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -1,4 +1,4 @@ -/* Management of a process's keyrings +/* Manage a process's keyrings   *   * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -21,41 +21,43 @@  #include <asm/uaccess.h>  #include "internal.h" -/* session keyring create vs join semaphore */ +/* Session keyring create vs join semaphore */  static DEFINE_MUTEX(key_session_mutex); -/* user keyring creation semaphore */ +/* User keyring creation semaphore */  static DEFINE_MUTEX(key_user_keyring_mutex); -/* the root user's tracking struct */ +/* The root user's tracking struct */  struct key_user root_key_user = {  	.usage		= ATOMIC_INIT(3),  	.cons_lock	= __MUTEX_INITIALIZER(root_key_user.cons_lock),  	.lock		= __SPIN_LOCK_UNLOCKED(root_key_user.lock),  	.nkeys		= ATOMIC_INIT(2),  	.nikeys		= ATOMIC_INIT(2), -	.uid		= 0, -	.user_ns	= &init_user_ns, +	.uid		= GLOBAL_ROOT_UID,  }; -/*****************************************************************************/  /* - * install user and user session keyrings for a particular UID + * Install the user and user session keyrings for the current process's UID.   */  int install_user_keyrings(void)  {  	struct user_struct *user;  	const struct cred *cred;  	struct key *uid_keyring, *session_keyring; +	key_perm_t user_keyring_perm;  	char buf[20];  	int ret; +	uid_t uid; +	user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;  	cred = current_cred();  	user = cred->user; +	uid = from_kuid(cred->user_ns, user->uid); -	kenter("%p{%u}", user, user->uid); +	kenter("%p{%u}", user, uid); -	if (user->uid_keyring) { +	if (user->uid_keyring && user->session_keyring) {  		kleave(" = 0 [exist]");  		return 0;  	} @@ -68,13 +70,13 @@ int install_user_keyrings(void)  		 * - there may be one in existence already as it may have been  		 *   pinned by a session, but the user_struct pointing to it  		 *   may have been destroyed by setuid */ -		sprintf(buf, "_uid.%u", user->uid); +		sprintf(buf, "_uid.%u", uid);  		uid_keyring = find_keyring_by_name(buf, true);  		if (IS_ERR(uid_keyring)) { -			uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, -						    cred, KEY_ALLOC_IN_QUOTA, -						    NULL); +			uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, +						    cred, user_keyring_perm, +						    KEY_ALLOC_IN_QUOTA, NULL);  			if (IS_ERR(uid_keyring)) {  				ret = PTR_ERR(uid_keyring);  				goto error; @@ -83,13 +85,14 @@ int install_user_keyrings(void)  		/* get a default session keyring (which might also exist  		 * already) */ -		sprintf(buf, "_uid_ses.%u", user->uid); +		sprintf(buf, "_uid_ses.%u", uid);  		session_keyring = find_keyring_by_name(buf, true);  		if (IS_ERR(session_keyring)) {  			session_keyring = -				keyring_alloc(buf, user->uid, (gid_t) -1, -					      cred, KEY_ALLOC_IN_QUOTA, NULL); +				keyring_alloc(buf, user->uid, INVALID_GID, +					      cred, user_keyring_perm, +					      KEY_ALLOC_IN_QUOTA, NULL);  			if (IS_ERR(session_keyring)) {  				ret = PTR_ERR(session_keyring);  				goto error_release; @@ -122,13 +125,15 @@ error:  }  /* - * install a fresh thread keyring directly to new credentials + * Install a fresh thread keyring directly to new credentials.  This keyring is + * allowed to overrun the quota.   */  int install_thread_keyring_to_cred(struct cred *new)  {  	struct key *keyring;  	keyring = keyring_alloc("_tid", new->uid, new->gid, new, +				KEY_POS_ALL | KEY_USR_VIEW,  				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	if (IS_ERR(keyring))  		return PTR_ERR(keyring); @@ -138,7 +143,7 @@ int install_thread_keyring_to_cred(struct cred *new)  }  /* - * install a fresh thread keyring, discarding the old one + * Install a fresh thread keyring, discarding the old one.   */  static int install_thread_keyring(void)  { @@ -161,39 +166,34 @@ static int install_thread_keyring(void)  }  /* - * install a process keyring directly to a credentials struct - * - returns -EEXIST if there was already a process keyring, 0 if one installed, - *   and other -ve on any other error + * Install a process keyring directly to a credentials struct. + * + * Returns -EEXIST if there was already a process keyring, 0 if one installed, + * and other value on any other error   */  int install_process_keyring_to_cred(struct cred *new)  {  	struct key *keyring; -	int ret; -	if (new->tgcred->process_keyring) +	if (new->process_keyring)  		return -EEXIST; -	keyring = keyring_alloc("_pid", new->uid, new->gid, -				new, KEY_ALLOC_QUOTA_OVERRUN, NULL); +	keyring = keyring_alloc("_pid", new->uid, new->gid, new, +				KEY_POS_ALL | KEY_USR_VIEW, +				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	if (IS_ERR(keyring))  		return PTR_ERR(keyring); -	spin_lock_irq(&new->tgcred->lock); -	if (!new->tgcred->process_keyring) { -		new->tgcred->process_keyring = keyring; -		keyring = NULL; -		ret = 0; -	} else { -		ret = -EEXIST; -	} -	spin_unlock_irq(&new->tgcred->lock); -	key_put(keyring); -	return ret; +	new->process_keyring = keyring; +	return 0;  }  /* - * make sure a process keyring is installed - * - we + * Make sure a process keyring is installed for the current process.  The + * existing process keyring is not replaced. + * + * Returns 0 if there is a process keyring by the end of this function, some + * error otherwise.   */  static int install_process_keyring(void)  { @@ -214,7 +214,7 @@ static int install_process_keyring(void)  }  /* - * install a session keyring directly to a credentials struct + * Install a session keyring directly to a credentials struct.   */  int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  { @@ -226,36 +226,31 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  	/* create an empty session keyring */  	if (!keyring) {  		flags = KEY_ALLOC_QUOTA_OVERRUN; -		if (cred->tgcred->session_keyring) +		if (cred->session_keyring)  			flags = KEY_ALLOC_IN_QUOTA; -		keyring = keyring_alloc("_ses", cred->uid, cred->gid, -					cred, flags, NULL); +		keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, +					KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, +					flags, NULL);  		if (IS_ERR(keyring))  			return PTR_ERR(keyring);  	} else { -		atomic_inc(&keyring->usage); +		__key_get(keyring);  	}  	/* install the keyring */ -	spin_lock_irq(&cred->tgcred->lock); -	old = cred->tgcred->session_keyring; -	rcu_assign_pointer(cred->tgcred->session_keyring, keyring); -	spin_unlock_irq(&cred->tgcred->lock); - -	/* we're using RCU on the pointer, but there's no point synchronising -	 * on it if it didn't previously point to anything */ -	if (old) { -		synchronize_rcu(); +	old = cred->session_keyring; +	rcu_assign_pointer(cred->session_keyring, keyring); + +	if (old)  		key_put(old); -	}  	return 0;  }  /* - * install a session keyring, discarding the old one - * - if a keyring is not supplied, an empty one is invented + * Install a session keyring, discarding the old one.  If a keyring is not + * supplied, an empty one is invented.   */  static int install_session_keyring(struct key *keyring)  { @@ -266,7 +261,7 @@ static int install_session_keyring(struct key *keyring)  	if (!new)  		return -ENOMEM; -	ret = install_session_keyring_to_cred(new, NULL); +	ret = install_session_keyring_to_cred(new, keyring);  	if (ret < 0) {  		abort_creds(new);  		return ret; @@ -275,9 +270,8 @@ static int install_session_keyring(struct key *keyring)  	return commit_creds(new);  } -/*****************************************************************************/  /* - * the filesystem user ID changed + * Handle the fsuid changing.   */  void key_fsuid_changed(struct task_struct *tsk)  { @@ -288,12 +282,10 @@ void key_fsuid_changed(struct task_struct *tsk)  		tsk->cred->thread_keyring->uid = tsk->cred->fsuid;  		up_write(&tsk->cred->thread_keyring->sem);  	} +} -} /* end key_fsuid_changed() */ - -/*****************************************************************************/  /* - * the filesystem group ID changed + * Handle the fsgid changing.   */  void key_fsgid_changed(struct task_struct *tsk)  { @@ -304,21 +296,30 @@ void key_fsgid_changed(struct task_struct *tsk)  		tsk->cred->thread_keyring->gid = tsk->cred->fsgid;  		up_write(&tsk->cred->thread_keyring->sem);  	} +} -} /* end key_fsgid_changed() */ - -/*****************************************************************************/  /* - * search only my process keyrings for the first matching key - * - we use the supplied match function to see if the description (or other - *   feature of interest) matches - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys + * Search the process keyrings attached to the supplied cred for the first + * matching key. + * + * The search criteria are the type and the match function.  The description is + * given to the match function as a parameter, but doesn't otherwise influence + * the search.  Typically the match function will compare the description + * parameter to the key's description. + * + * This can only search keyrings that grant Search permission to the supplied + * credentials.  Keyrings linked to searched keyrings will also be searched if + * they grant Search permission too.  Keys can only be found if they grant + * Search permission to the credentials. + * + * Returns a pointer to the key with the key usage count incremented if + * successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only + * matched negative keys. + * + * In the case of a successful return, the possession attribute is set on the + * returned key reference.   */ -key_ref_t search_my_process_keyrings(struct key_type *type, -				     const void *description, -				     key_match_func_t match, -				     const struct cred *cred) +key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)  {  	key_ref_t key_ref, ret, err; @@ -334,17 +335,14 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	err = ERR_PTR(-EAGAIN);  	/* search the thread keyring first */ -	if (cred->thread_keyring) { +	if (ctx->cred->thread_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->thread_keyring, 1), -			cred, type, description, match); +			make_key_ref(ctx->cred->thread_keyring, 1), ctx);  		if (!IS_ERR(key_ref))  			goto found;  		switch (PTR_ERR(key_ref)) {  		case -EAGAIN: /* no key */ -			if (ret) -				break;  		case -ENOKEY: /* negative key */  			ret = key_ref;  			break; @@ -355,10 +353,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the process keyring second */ -	if (cred->tgcred->process_keyring) { +	if (ctx->cred->process_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->tgcred->process_keyring, 1), -			cred, type, description, match); +			make_key_ref(ctx->cred->process_keyring, 1), ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -376,13 +373,11 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the session keyring */ -	if (cred->tgcred->session_keyring) { +	if (ctx->cred->session_keyring) {  		rcu_read_lock();  		key_ref = keyring_search_aux( -			make_key_ref(rcu_dereference( -					     cred->tgcred->session_keyring), -				     1), -			cred, type, description, match); +			make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1), +			ctx);  		rcu_read_unlock();  		if (!IS_ERR(key_ref)) @@ -401,10 +396,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  		}  	}  	/* or search the user-session keyring */ -	else if (cred->user->session_keyring) { +	else if (ctx->cred->user->session_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->user->session_keyring, 1), -			cred, type, description, match); +			make_key_ref(ctx->cred->user->session_keyring, 1), +			ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -428,25 +423,22 @@ found:  	return key_ref;  } -/*****************************************************************************/  /* - * search the process keyrings for the first matching key - * - we use the supplied match function to see if the description (or other - *   feature of interest) matches - * - we return -EAGAIN if we didn't find any matching key - * - we return -ENOKEY if we found only negative matching keys + * Search the process keyrings attached to the supplied cred for the first + * matching key in the manner of search_my_process_keyrings(), but also search + * the keys attached to the assumed authorisation key using its credentials if + * one is available. + * + * Return same as search_my_process_keyrings().   */ -key_ref_t search_process_keyrings(struct key_type *type, -				  const void *description, -				  key_match_func_t match, -				  const struct cred *cred) +key_ref_t search_process_keyrings(struct keyring_search_context *ctx)  {  	struct request_key_auth *rka;  	key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;  	might_sleep(); -	key_ref = search_my_process_keyrings(type, description, match, cred); +	key_ref = search_my_process_keyrings(ctx);  	if (!IS_ERR(key_ref))  		goto found;  	err = key_ref; @@ -455,18 +447,21 @@ key_ref_t search_process_keyrings(struct key_type *type,  	 * search the keyrings of the process mentioned there  	 * - we don't permit access to request_key auth keys via this method  	 */ -	if (cred->request_key_auth && -	    cred == current_cred() && -	    type != &key_type_request_key_auth +	if (ctx->cred->request_key_auth && +	    ctx->cred == current_cred() && +	    ctx->index_key.type != &key_type_request_key_auth  	    ) { +		const struct cred *cred = ctx->cred; +  		/* defend against the auth key being revoked */  		down_read(&cred->request_key_auth->sem); -		if (key_validate(cred->request_key_auth) == 0) { -			rka = cred->request_key_auth->payload.data; +		if (key_validate(ctx->cred->request_key_auth) == 0) { +			rka = ctx->cred->request_key_auth->payload.data; -			key_ref = search_process_keyrings(type, description, -							  match, rka->cred); +			ctx->cred = rka->cred; +			key_ref = search_process_keyrings(ctx); +			ctx->cred = cred;  			up_read(&cred->request_key_auth->sem); @@ -489,41 +484,54 @@ key_ref_t search_process_keyrings(struct key_type *type,  found:  	return key_ref; +} -} /* end search_process_keyrings() */ - -/*****************************************************************************/  /* - * see if the key we're looking at is the target key + * See if the key we're looking at is the target key.   */  int lookup_user_key_possessed(const struct key *key, const void *target)  {  	return key == target; +} -} /* end lookup_user_key_possessed() */ - -/*****************************************************************************/  /* - * lookup a key given a key ID from userspace with a given permissions mask - * - don't create special keyrings unless so requested - * - partially constructed keys aren't found unless requested + * Look up a key ID given us by userspace with a given permissions mask to get + * the key it refers to. + * + * Flags can be passed to request that special keyrings be created if referred + * to directly, to permit partially constructed keys to be found and to skip + * validity and permission checks on the found key. + * + * Returns a pointer to the key with an incremented usage count if successful; + * -EINVAL if the key ID is invalid; -ENOKEY if the key ID does not correspond + * to a key or the best found key was a negative key; -EKEYREVOKED or + * -EKEYEXPIRED if the best found key was revoked or expired; -EACCES if the + * found key doesn't grant the requested permit or the LSM denied access to it; + * or -ENOMEM if a special keyring couldn't be created. + * + * In the case of a successful return, the possession attribute is set on the + * returned key reference.   */  key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,  			  key_perm_t perm)  { +	struct keyring_search_context ctx = { +		.match	= lookup_user_key_possessed, +		.flags	= (KEYRING_SEARCH_NO_STATE_CHECK | +			   KEYRING_SEARCH_LOOKUP_DIRECT), +	};  	struct request_key_auth *rka; -	const struct cred *cred;  	struct key *key;  	key_ref_t key_ref, skey_ref;  	int ret;  try_again: -	cred = get_current_cred(); +	ctx.cred = get_current_cred();  	key_ref = ERR_PTR(-ENOKEY);  	switch (id) {  	case KEY_SPEC_THREAD_KEYRING: -		if (!cred->thread_keyring) { +		if (!ctx.cred->thread_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -535,13 +543,13 @@ try_again:  			goto reget_creds;  		} -		key = cred->thread_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->thread_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_PROCESS_KEYRING: -		if (!cred->tgcred->process_keyring) { +		if (!ctx.cred->process_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -553,54 +561,64 @@ try_again:  			goto reget_creds;  		} -		key = cred->tgcred->process_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->process_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_SESSION_KEYRING: -		if (!cred->tgcred->session_keyring) { +		if (!ctx.cred->session_keyring) {  			/* always install a session keyring upon access if one  			 * doesn't exist yet */  			ret = install_user_keyrings();  			if (ret < 0)  				goto error; -			ret = install_session_keyring( -				cred->user->session_keyring); +			if (lflags & KEY_LOOKUP_CREATE) +				ret = join_session_keyring(NULL); +			else +				ret = install_session_keyring( +					ctx.cred->user->session_keyring);  			if (ret < 0)  				goto error;  			goto reget_creds; +		} else if (ctx.cred->session_keyring == +			   ctx.cred->user->session_keyring && +			   lflags & KEY_LOOKUP_CREATE) { +			ret = join_session_keyring(NULL); +			if (ret < 0) +				goto error; +			goto reget_creds;  		}  		rcu_read_lock(); -		key = rcu_dereference(cred->tgcred->session_keyring); -		atomic_inc(&key->usage); +		key = rcu_dereference(ctx.cred->session_keyring); +		__key_get(key);  		rcu_read_unlock();  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_USER_KEYRING: -		if (!cred->user->uid_keyring) { +		if (!ctx.cred->user->uid_keyring) {  			ret = install_user_keyrings();  			if (ret < 0)  				goto error;  		} -		key = cred->user->uid_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->user->uid_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_USER_SESSION_KEYRING: -		if (!cred->user->session_keyring) { +		if (!ctx.cred->user->session_keyring) {  			ret = install_user_keyrings();  			if (ret < 0)  				goto error;  		} -		key = cred->user->session_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->user->session_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break; @@ -610,28 +628,29 @@ try_again:  		goto error;  	case KEY_SPEC_REQKEY_AUTH_KEY: -		key = cred->request_key_auth; +		key = ctx.cred->request_key_auth;  		if (!key)  			goto error; -		atomic_inc(&key->usage); +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_REQUESTOR_KEYRING: -		if (!cred->request_key_auth) +		if (!ctx.cred->request_key_auth)  			goto error; -		down_read(&cred->request_key_auth->sem); -		if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) { +		down_read(&ctx.cred->request_key_auth->sem); +		if (test_bit(KEY_FLAG_REVOKED, +			     &ctx.cred->request_key_auth->flags)) {  			key_ref = ERR_PTR(-EKEYREVOKED);  			key = NULL;  		} else { -			rka = cred->request_key_auth->payload.data; +			rka = ctx.cred->request_key_auth->payload.data;  			key = rka->dest_keyring; -			atomic_inc(&key->usage); +			__key_get(key);  		} -		up_read(&cred->request_key_auth->sem); +		up_read(&ctx.cred->request_key_auth->sem);  		if (!key)  			goto error;  		key_ref = make_key_ref(key, 1); @@ -651,9 +670,13 @@ try_again:  		key_ref = make_key_ref(key, 0);  		/* check to see if we possess the key */ -		skey_ref = search_process_keyrings(key->type, key, -						   lookup_user_key_possessed, -						   cred); +		ctx.index_key.type		= key->type; +		ctx.index_key.description	= key->description; +		ctx.index_key.desc_len		= strlen(key->description); +		ctx.match_data			= key; +		kdebug("check possessed"); +		skey_ref = search_process_keyrings(&ctx); +		kdebug("possessed=%p", skey_ref);  		if (!IS_ERR(skey_ref)) {  			key_put(key); @@ -693,12 +716,14 @@ try_again:  		goto invalid_key;  	/* check the permissions */ -	ret = key_task_permission(key_ref, cred, perm); +	ret = key_task_permission(key_ref, ctx.cred, perm);  	if (ret < 0)  		goto invalid_key; +	key->last_used_at = current_kernel_time().tv_sec; +  error: -	put_cred(cred); +	put_cred(ctx.cred);  	return key_ref;  invalid_key: @@ -709,17 +734,20 @@ invalid_key:  	/* if we attempted to install a keyring, then it may have caused new  	 * creds to be installed */  reget_creds: -	put_cred(cred); +	put_cred(ctx.cred);  	goto try_again; +} -} /* end lookup_user_key() */ - -/*****************************************************************************/  /* - * join the named keyring as the session keyring if possible, or attempt to - * create a new one of that name if not - * - if the name is NULL, an empty anonymous keyring is installed instead - * - named session keyring joining is done with a semaphore held + * Join the named keyring as the session keyring if possible else attempt to + * create a new one of that name and join that. + * + * If the name is NULL, an empty anonymous keyring will be installed as the + * session keyring. + * + * Named session keyrings are joined with a semaphore held to prevent the + * keyrings from going away whilst the attempt is made to going them and also + * to prevent a race in creating compatible session keyrings.   */  long join_session_keyring(const char *name)  { @@ -728,12 +756,6 @@ long join_session_keyring(const char *name)  	struct key *keyring;  	long ret, serial; -	/* only permit this if there's a single thread in the thread group - -	 * this avoids us having to adjust the creds on all threads and risking -	 * ENOMEM */ -	if (!current_is_single_threaded()) -		return -EMLINK; -  	new = prepare_creds();  	if (!new)  		return -ENOMEM; @@ -745,7 +767,7 @@ long join_session_keyring(const char *name)  		if (ret < 0)  			goto error; -		serial = new->tgcred->session_keyring->serial; +		serial = new->session_keyring->serial;  		ret = commit_creds(new);  		if (ret == 0)  			ret = serial; @@ -759,8 +781,10 @@ long join_session_keyring(const char *name)  	keyring = find_keyring_by_name(name, false);  	if (PTR_ERR(keyring) == -ENOKEY) {  		/* not found - try and create a new one */ -		keyring = keyring_alloc(name, old->uid, old->gid, old, -					KEY_ALLOC_IN_QUOTA, NULL); +		keyring = keyring_alloc( +			name, old->uid, old->gid, old, +			KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, +			KEY_ALLOC_IN_QUOTA, NULL);  		if (IS_ERR(keyring)) {  			ret = PTR_ERR(keyring);  			goto error2; @@ -768,6 +792,9 @@ long join_session_keyring(const char *name)  	} else if (IS_ERR(keyring)) {  		ret = PTR_ERR(keyring);  		goto error2; +	} else if (keyring == new->session_keyring) { +		ret = 0; +		goto error2;  	}  	/* we've got a keyring - now to install it */ @@ -791,26 +818,19 @@ error:  }  /* - * Replace a process's session keyring when that process resumes userspace on - * behalf of one of its children + * Replace a process's session keyring on behalf of one of its children when + * the target  process is about to resume userspace execution.   */ -void key_replace_session_keyring(void) +void key_change_session_keyring(struct callback_head *twork)  { -	const struct cred *old; -	struct cred *new; +	const struct cred *old = current_cred(); +	struct cred *new = container_of(twork, struct cred, rcu); -	if (!current->replacement_session_keyring) -		return; - -	write_lock_irq(&tasklist_lock); -	new = current->replacement_session_keyring; -	current->replacement_session_keyring = NULL; -	write_unlock_irq(&tasklist_lock); - -	if (!new) +	if (unlikely(current->flags & PF_EXITING)) { +		put_cred(new);  		return; +	} -	old = current_cred();  	new->  uid	= old->  uid;  	new-> euid	= old-> euid;  	new-> suid	= old-> suid; @@ -820,6 +840,7 @@ void key_replace_session_keyring(void)  	new-> sgid	= old-> sgid;  	new->fsgid	= old->fsgid;  	new->user	= get_uid(old->user); +	new->user_ns	= get_user_ns(old->user_ns);  	new->group_info	= get_group_info(old->group_info);  	new->securebits	= old->securebits; @@ -830,10 +851,19 @@ void key_replace_session_keyring(void)  	new->jit_keyring	= old->jit_keyring;  	new->thread_keyring	= key_get(old->thread_keyring); -	new->tgcred->tgid	= old->tgcred->tgid; -	new->tgcred->process_keyring = key_get(old->tgcred->process_keyring); +	new->process_keyring	= key_get(old->process_keyring);  	security_transfer_creds(new, old);  	commit_creds(new);  } + +/* + * Make sure that root's user and user-session keyrings exist. + */ +static int __init init_root_keyring(void) +{ +	return install_user_keyrings(); +} + +late_initcall(init_root_keyring); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 0088dd8bf68..381411941cc 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -8,7 +8,7 @@   * as published by the Free Software Foundation; either version   * 2 of the License, or (at your option) any later version.   * - * See Documentation/keys-request-key.txt + * See Documentation/security/keys-request-key.txt   */  #include <linux/module.h> @@ -39,8 +39,14 @@ static int key_wait_bit_intr(void *flags)  	return signal_pending(current) ? -ERESTARTSYS : 0;  } -/* - * call to complete the construction of a key +/** + * complete_request_key - Complete the construction of a key. + * @cons: The key construction record. + * @error: The success or failute of the construction. + * + * Complete the attempt to construct a key.  The key will be negated + * if an error is indicated.  The authorisation key will be revoked + * unconditionally.   */  void complete_request_key(struct key_construction *cons, int error)  { @@ -58,40 +64,49 @@ void complete_request_key(struct key_construction *cons, int error)  }  EXPORT_SYMBOL(complete_request_key); -static int umh_keys_init(struct subprocess_info *info) +/* + * Initialise a usermode helper that is going to have a specific session + * keyring. + * + * This is called in context of freshly forked kthread before kernel_execve(), + * so we can simply install the desired session_keyring at this point. + */ +static int umh_keys_init(struct subprocess_info *info, struct cred *cred)  { -	struct cred *cred = (struct cred*)current_cred();  	struct key *keyring = info->data; -	/* -	 * This is called in context of freshly forked kthread before -	 * kernel_execve(), we can just change our ->session_keyring. -	 */ +  	return install_session_keyring_to_cred(cred, keyring);  } +/* + * Clean up a usermode helper with session keyring. + */  static void umh_keys_cleanup(struct subprocess_info *info)  {  	struct key *keyring = info->data;  	key_put(keyring);  } +/* + * Call a usermode helper with a specific session keyring. + */  static int call_usermodehelper_keys(char *path, char **argv, char **envp, -			 struct key *session_keyring, enum umh_wait wait) +					struct key *session_keyring, int wait)  { -	gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; -	struct subprocess_info *info = -		call_usermodehelper_setup(path, argv, envp, gfp_mask); +	struct subprocess_info *info; +	info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL, +					  umh_keys_init, umh_keys_cleanup, +					  session_keyring);  	if (!info)  		return -ENOMEM; -	call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup, -					key_get(session_keyring)); +	key_get(session_keyring);  	return call_usermodehelper_exec(info, wait);  }  /* - * request userspace finish the construction of a key + * Request userspace finish the construction of a key   * - execute "/sbin/request-key <op> <key> <uid> <gid> <keyring> <keyring> <keyring>"   */  static int call_sbin_request_key(struct key_construction *cons, @@ -118,6 +133,7 @@ static int call_sbin_request_key(struct key_construction *cons,  	cred = get_current_cred();  	keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred, +				KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,  				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	put_cred(cred);  	if (IS_ERR(keyring)) { @@ -131,8 +147,8 @@ static int call_sbin_request_key(struct key_construction *cons,  		goto error_link;  	/* record the UID and GID */ -	sprintf(uid_str, "%d", cred->fsuid); -	sprintf(gid_str, "%d", cred->fsgid); +	sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid)); +	sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid));  	/* we say which key is under construction */  	sprintf(key_str, "%d", key->serial); @@ -142,12 +158,12 @@ static int call_sbin_request_key(struct key_construction *cons,  		cred->thread_keyring ? cred->thread_keyring->serial : 0);  	prkey = 0; -	if (cred->tgcred->process_keyring) -		prkey = cred->tgcred->process_keyring->serial; +	if (cred->process_keyring) +		prkey = cred->process_keyring->serial;  	sprintf(keyring_str[1], "%d", prkey);  	rcu_read_lock(); -	session = rcu_dereference(cred->tgcred->session_keyring); +	session = rcu_dereference(cred->session_keyring);  	if (!session)  		session = cred->user->session_keyring;  	sskey = session->serial; @@ -198,8 +214,9 @@ error_alloc:  }  /* - * call out to userspace for key construction - * - we ignore program failure and go on key status instead + * Call out to userspace for key construction. + * + * Program failure is ignored in favour of key status.   */  static int construct_key(struct key *key, const void *callout_info,  			 size_t callout_len, void *aux, @@ -246,9 +263,10 @@ static int construct_key(struct key *key, const void *callout_info,  }  /* - * get the appropriate destination keyring for the request - * - we return whatever keyring we select with an extra reference upon it which - *   the caller must release + * Get the appropriate destination keyring for the request. + * + * The keyring selected is returned with an extra reference upon it which the + * caller must release.   */  static void construct_get_dest_keyring(struct key **_dest_keyring)  { @@ -287,14 +305,14 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  				break;  		case KEY_REQKEY_DEFL_PROCESS_KEYRING: -			dest_keyring = key_get(cred->tgcred->process_keyring); +			dest_keyring = key_get(cred->process_keyring);  			if (dest_keyring)  				break;  		case KEY_REQKEY_DEFL_SESSION_KEYRING:  			rcu_read_lock();  			dest_keyring = key_get( -				rcu_dereference(cred->tgcred->session_keyring)); +				rcu_dereference(cred->session_keyring));  			rcu_read_unlock();  			if (dest_keyring) @@ -321,38 +339,48 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  }  /* - * allocate a new key in under-construction state and attempt to link it in to - * the requested place - * - may return a key that's already under construction instead + * Allocate a new key in under-construction state and attempt to link it in to + * the requested keyring. + * + * May return a key that's already under construction instead if there was a + * race between two thread calling request_key().   */ -static int construct_alloc_key(struct key_type *type, -			       const char *description, +static int construct_alloc_key(struct keyring_search_context *ctx,  			       struct key *dest_keyring,  			       unsigned long flags,  			       struct key_user *user,  			       struct key **_key)  { -	struct keyring_list *prealloc; -	const struct cred *cred = current_cred(); +	struct assoc_array_edit *edit;  	struct key *key; +	key_perm_t perm;  	key_ref_t key_ref;  	int ret; -	kenter("%s,%s,,,", type->name, description); +	kenter("%s,%s,,,", +	       ctx->index_key.type->name, ctx->index_key.description);  	*_key = NULL;  	mutex_lock(&user->cons_lock); -	key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred, -			KEY_POS_ALL, flags); +	perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR; +	perm |= KEY_USR_VIEW; +	if (ctx->index_key.type->read) +		perm |= KEY_POS_READ; +	if (ctx->index_key.type == &key_type_keyring || +	    ctx->index_key.type->update) +		perm |= KEY_POS_WRITE; + +	key = key_alloc(ctx->index_key.type, ctx->index_key.description, +			ctx->cred->fsuid, ctx->cred->fsgid, ctx->cred, +			perm, flags);  	if (IS_ERR(key))  		goto alloc_failed;  	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);  	if (dest_keyring) { -		ret = __key_link_begin(dest_keyring, type, description, -				       &prealloc); +		ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit);  		if (ret < 0)  			goto link_prealloc_failed;  	} @@ -362,16 +390,16 @@ static int construct_alloc_key(struct key_type *type,  	 * waited for locks */  	mutex_lock(&key_construction_mutex); -	key_ref = search_process_keyrings(type, description, type->match, cred); +	key_ref = search_process_keyrings(ctx);  	if (!IS_ERR(key_ref))  		goto key_already_present;  	if (dest_keyring) -		__key_link(dest_keyring, key, &prealloc); +		__key_link(key, &edit);  	mutex_unlock(&key_construction_mutex);  	if (dest_keyring) -		__key_link_end(dest_keyring, type, prealloc); +		__key_link_end(dest_keyring, &ctx->index_key, edit);  	mutex_unlock(&user->cons_lock);  	*_key = key;  	kleave(" = 0 [%d]", key_serial(key)); @@ -386,8 +414,8 @@ key_already_present:  	if (dest_keyring) {  		ret = __key_link_check_live_key(dest_keyring, key);  		if (ret == 0) -			__key_link(dest_keyring, key, &prealloc); -		__key_link_end(dest_keyring, type, prealloc); +			__key_link(key, &edit); +		__key_link_end(dest_keyring, &ctx->index_key, edit);  		if (ret < 0)  			goto link_check_failed;  	} @@ -403,7 +431,6 @@ link_check_failed:  	return ret;  link_prealloc_failed: -	up_write(&dest_keyring->sem);  	mutex_unlock(&user->cons_lock);  	kleave(" = %d [prelink]", ret);  	return ret; @@ -415,10 +442,9 @@ alloc_failed:  }  /* - * commence key construction + * Commence key construction.   */ -static struct key *construct_key_and_link(struct key_type *type, -					  const char *description, +static struct key *construct_key_and_link(struct keyring_search_context *ctx,  					  const char *callout_info,  					  size_t callout_len,  					  void *aux, @@ -431,14 +457,13 @@ static struct key *construct_key_and_link(struct key_type *type,  	kenter(""); -	user = key_user_lookup(current_fsuid(), current_user_ns()); +	user = key_user_lookup(current_fsuid());  	if (!user)  		return ERR_PTR(-ENOMEM);  	construct_get_dest_keyring(&dest_keyring); -	ret = construct_alloc_key(type, description, dest_keyring, flags, user, -				  &key); +	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);  	key_user_put(user);  	if (ret == 0) { @@ -451,7 +476,7 @@ static struct key *construct_key_and_link(struct key_type *type,  	} else if (ret == -EINPROGRESS) {  		ret = 0;  	} else { -		key = ERR_PTR(ret); +		goto couldnt_alloc_key;  	}  	key_put(dest_keyring); @@ -461,17 +486,38 @@ static struct key *construct_key_and_link(struct key_type *type,  construction_failed:  	key_negate_and_link(key, key_negative_timeout, NULL, NULL);  	key_put(key); +couldnt_alloc_key:  	key_put(dest_keyring);  	kleave(" = %d", ret);  	return ERR_PTR(ret);  } -/* - * request a key - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - cache the key in an appropriate keyring +/** + * request_key_and_link - Request a key and cache it in a keyring. + * @type: The type of key we want. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * @dest_keyring: Where to cache the key. + * @flags: Flags to key_alloc(). + * + * A key matching the specified criteria is searched for in the process's + * keyrings and returned with its usage count incremented if found.  Otherwise, + * if callout_info is not NULL, a key will be allocated and some service + * (probably in userspace) will be asked to instantiate it. + * + * If successfully found or created, the key will be linked to the destination + * keyring if one is provided. + * + * Returns a pointer to the key if successful; -EACCES, -ENOKEY, -EKEYREVOKED + * or -EKEYEXPIRED if an inaccessible, negative, revoked or expired key was + * found; -ENOKEY if no key was found and no @callout_info was given; -EDQUOT + * if insufficient key quota was available to create a new key; or -ENOMEM if + * insufficient memory was available. + * + * If the returned key was created, then it may still be under construction, + * and wait_for_key_construction() should be used to wait for that to complete.   */  struct key *request_key_and_link(struct key_type *type,  				 const char *description, @@ -481,18 +527,24 @@ struct key *request_key_and_link(struct key_type *type,  				 struct key *dest_keyring,  				 unsigned long flags)  { -	const struct cred *cred = current_cred(); +	struct keyring_search_context ctx = { +		.index_key.type		= type, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= type->match, +		.match_data		= description, +		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT, +	};  	struct key *key;  	key_ref_t key_ref;  	int ret;  	kenter("%s,%s,%p,%zu,%p,%p,%lx", -	       type->name, description, callout_info, callout_len, aux, -	       dest_keyring, flags); +	       ctx.index_key.type->name, ctx.index_key.description, +	       callout_info, callout_len, aux, dest_keyring, flags);  	/* search all the process keyrings for a key */ -	key_ref = search_process_keyrings(type, description, type->match, -					  cred); +	key_ref = search_process_keyrings(&ctx);  	if (!IS_ERR(key_ref)) {  		key = key_ref_to_ptr(key_ref); @@ -515,9 +567,8 @@ struct key *request_key_and_link(struct key_type *type,  		if (!callout_info)  			goto error; -		key = construct_key_and_link(type, description, callout_info, -					     callout_len, aux, dest_keyring, -					     flags); +		key = construct_key_and_link(&ctx, callout_info, callout_len, +					     aux, dest_keyring, flags);  	}  error: @@ -525,8 +576,16 @@ error:  	return key;  } -/* - * wait for construction of a key to complete +/** + * wait_for_key_construction - Wait for construction of a key to complete + * @key: The key being waited for. + * @intr: Whether to wait interruptibly. + * + * Wait for a key to finish being constructed. + * + * Returns 0 if successful; -ERESTARTSYS if the wait was interrupted; -ENOKEY + * if the key was negated; or -EKEYREVOKED or -EKEYEXPIRED if the key was + * revoked or expired.   */  int wait_for_key_construction(struct key *key, bool intr)  { @@ -537,18 +596,27 @@ int wait_for_key_construction(struct key *key, bool intr)  			  intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);  	if (ret < 0)  		return ret; -	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) -		return -ENOKEY; +	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { +		smp_rmb(); +		return key->type_data.reject_error; +	}  	return key_validate(key);  }  EXPORT_SYMBOL(wait_for_key_construction); -/* - * request a key - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - waits uninterruptible for creation to complete +/** + * request_key - Request a key and wait for construction + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found, new keys are always allocated in the user's quota, + * the callout_info must be a NUL-terminated string and no auxiliary data can + * be passed. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait.   */  struct key *request_key(struct key_type *type,  			const char *description, @@ -573,12 +641,19 @@ struct key *request_key(struct key_type *type,  }  EXPORT_SYMBOL(request_key); -/* - * request a key with auxiliary data for the upcaller - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided - * - waits uninterruptible for creation to complete +/** + * request_key_with_auxdata - Request a key with auxiliary data for the upcaller + * @type: The type of key we want. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found and new keys are always allocated in the user's quota. + * + * Furthermore, it then works as wait_for_key_construction() to wait for the + * completion of keys undergoing construction with a non-interruptible wait.   */  struct key *request_key_with_auxdata(struct key_type *type,  				     const char *description, @@ -603,10 +678,18 @@ struct key *request_key_with_auxdata(struct key_type *type,  EXPORT_SYMBOL(request_key_with_auxdata);  /* - * request a key (allow async construction) - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided + * request_key_async - Request a key (allow async construction) + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found, new keys are always allocated in the user's quota and + * no auxiliary data can be passed. + * + * The caller should call wait_for_key_construction() to wait for the + * completion of the returned key if it is still undergoing construction.   */  struct key *request_key_async(struct key_type *type,  			      const char *description, @@ -621,9 +704,17 @@ EXPORT_SYMBOL(request_key_async);  /*   * request a key with auxiliary data for the upcaller (allow async construction) - * - search the process's keyrings - * - check the list of keys being created or updated - * - call out to userspace for a key if supplementary info was provided + * @type: Type of key. + * @description: The searchable description of the key. + * @callout_info: The data to pass to the instantiation upcall (or NULL). + * @callout_len: The length of callout_info. + * @aux: Auxiliary data for the upcall. + * + * As for request_key_and_link() except that it does not add the returned key + * to a keyring if found and new keys are always allocated in the user's quota. + * + * The caller should call wait_for_key_construction() to wait for the + * completion of the returned key if it is still undergoing construction.   */  struct key *request_key_async_with_auxdata(struct key_type *type,  					   const char *description, diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 86747151ee5..7495a93b4b9 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -1,4 +1,4 @@ -/* request_key_auth.c: request key authorisation controlling key def +/* Request key authorisation token key definition.   *   * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -8,7 +8,7 @@   * as published by the Free Software Foundation; either version   * 2 of the License, or (at your option) any later version.   * - * See Documentation/keys-request-key.txt + * See Documentation/security/keys-request-key.txt   */  #include <linux/module.h> @@ -18,15 +18,17 @@  #include <linux/slab.h>  #include <asm/uaccess.h>  #include "internal.h" +#include <keys/user-type.h> -static int request_key_auth_instantiate(struct key *, const void *, size_t); +static int request_key_auth_instantiate(struct key *, +					struct key_preparsed_payload *);  static void request_key_auth_describe(const struct key *, struct seq_file *);  static void request_key_auth_revoke(struct key *);  static void request_key_auth_destroy(struct key *);  static long request_key_auth_read(const struct key *, char __user *, size_t);  /* - * the request-key authorisation key type definition + * The request-key authorisation key type definition.   */  struct key_type key_type_request_key_auth = {  	.name		= ".request_key_auth", @@ -38,22 +40,18 @@ struct key_type key_type_request_key_auth = {  	.read		= request_key_auth_read,  }; -/*****************************************************************************/  /* - * instantiate a request-key authorisation key + * Instantiate a request-key authorisation key.   */  static int request_key_auth_instantiate(struct key *key, -					const void *data, -					size_t datalen) +					struct key_preparsed_payload *prep)  { -	key->payload.data = (struct request_key_auth *) data; +	key->payload.data = (struct request_key_auth *)prep->data;  	return 0; +} -} /* end request_key_auth_instantiate() */ - -/*****************************************************************************/  /* - * reading a request-key authorisation key retrieves the callout information + * Describe an authorisation token.   */  static void request_key_auth_describe(const struct key *key,  				      struct seq_file *m) @@ -62,13 +60,12 @@ static void request_key_auth_describe(const struct key *key,  	seq_puts(m, "key:");  	seq_puts(m, key->description); -	seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); - -} /* end request_key_auth_describe() */ +	if (key_is_instantiated(key)) +		seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len); +} -/*****************************************************************************/  /* - * read the callout_info data + * Read the callout_info data (retrieves the callout information).   * - the key's semaphore is read-locked   */  static long request_key_auth_read(const struct key *key, @@ -91,13 +88,12 @@ static long request_key_auth_read(const struct key *key,  	}  	return ret; +} -} /* end request_key_auth_read() */ - -/*****************************************************************************/  /* - * handle revocation of an authorisation token key - * - called with the key sem write-locked + * Handle revocation of an authorisation token key. + * + * Called with the key sem write-locked.   */  static void request_key_auth_revoke(struct key *key)  { @@ -109,12 +105,10 @@ static void request_key_auth_revoke(struct key *key)  		put_cred(rka->cred);  		rka->cred = NULL;  	} +} -} /* end request_key_auth_revoke() */ - -/*****************************************************************************/  /* - * destroy an instantiation authorisation token key + * Destroy an instantiation authorisation token key.   */  static void request_key_auth_destroy(struct key *key)  { @@ -131,13 +125,11 @@ static void request_key_auth_destroy(struct key *key)  	key_put(rka->dest_keyring);  	kfree(rka->callout_info);  	kfree(rka); +} -} /* end request_key_auth_destroy() */ - -/*****************************************************************************/  /* - * create an authorisation token for /sbin/request-key or whoever to gain - * access to the caller's security data + * Create an authorisation token for /sbin/request-key or whoever to gain + * access to the caller's security data.   */  struct key *request_key_auth_new(struct key *target, const void *callout_info,  				 size_t callout_len, struct key *dest_keyring) @@ -228,45 +220,34 @@ error_alloc:  	kfree(rka);  	kleave("= %d", ret);  	return ERR_PTR(ret); +} -} /* end request_key_auth_new() */ - -/*****************************************************************************/ -/* - * see if an authorisation key is associated with a particular key - */ -static int key_get_instantiation_authkey_match(const struct key *key, -					       const void *_id) -{ -	struct request_key_auth *rka = key->payload.data; -	key_serial_t id = (key_serial_t)(unsigned long) _id; - -	return rka->target_key->serial == id; - -} /* end key_get_instantiation_authkey_match() */ - -/*****************************************************************************/  /* - * get the authorisation key for instantiation of a specific key if attached to - * the current process's keyrings - * - this key is inserted into a keyring and that is set as /sbin/request-key's - *   session keyring - * - a target_id of zero specifies any valid token + * Search the current process's keyrings for the authorisation key for + * instantiation of a key.   */  struct key *key_get_instantiation_authkey(key_serial_t target_id)  { -	const struct cred *cred = current_cred(); +	char description[16]; +	struct keyring_search_context ctx = { +		.index_key.type		= &key_type_request_key_auth, +		.index_key.description	= description, +		.cred			= current_cred(), +		.match			= user_match, +		.match_data		= description, +		.flags			= KEYRING_SEARCH_LOOKUP_DIRECT, +	};  	struct key *authkey;  	key_ref_t authkey_ref; -	authkey_ref = search_process_keyrings( -		&key_type_request_key_auth, -		(void *) (unsigned long) target_id, -		key_get_instantiation_authkey_match, -		cred); +	sprintf(description, "%x", target_id); + +	authkey_ref = search_process_keyrings(&ctx);  	if (IS_ERR(authkey_ref)) {  		authkey = ERR_CAST(authkey_ref); +		if (authkey == ERR_PTR(-EAGAIN)) +			authkey = ERR_PTR(-ENOKEY);  		goto error;  	} @@ -278,5 +259,4 @@ struct key *key_get_instantiation_authkey(key_serial_t target_id)  error:  	return authkey; - -} /* end key_get_instantiation_authkey() */ +} diff --git a/security/keys/sysctl.c b/security/keys/sysctl.c index ee32d181764..b68faa1a5cf 100644 --- a/security/keys/sysctl.c +++ b/security/keys/sysctl.c @@ -15,7 +15,7 @@  static const int zero, one = 1, max = INT_MAX; -ctl_table key_sysctls[] = { +struct ctl_table key_sysctls[] = {  	{  		.procname = "maxkeys",  		.data = &key_quota_maxkeys, @@ -61,5 +61,16 @@ ctl_table key_sysctls[] = {  		.extra1 = (void *) &zero,  		.extra2 = (void *) &max,  	}, +#ifdef CONFIG_PERSISTENT_KEYRINGS +	{ +		.procname = "persistent_keyring_expiry", +		.data = &persistent_keyring_expiry, +		.maxlen = sizeof(unsigned), +		.mode = 0644, +		.proc_handler = proc_dointvec_minmax, +		.extra1 = (void *) &zero, +		.extra2 = (void *) &max, +	}, +#endif  	{ }  }; diff --git a/security/keys/trusted.c b/security/keys/trusted.c new file mode 100644 index 00000000000..6b804aa4529 --- /dev/null +++ b/security/keys/trusted.c @@ -0,0 +1,1163 @@ +/* + * Copyright (C) 2010 IBM Corporation + * + * Author: + * David Safford <safford@us.ibm.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, version 2 of the License. + * + * See Documentation/security/keys-trusted-encrypted.txt + */ + +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/parser.h> +#include <linux/string.h> +#include <linux/err.h> +#include <keys/user-type.h> +#include <keys/trusted-type.h> +#include <linux/key-type.h> +#include <linux/rcupdate.h> +#include <linux/crypto.h> +#include <crypto/hash.h> +#include <crypto/sha.h> +#include <linux/capability.h> +#include <linux/tpm.h> +#include <linux/tpm_command.h> + +#include "trusted.h" + +static const char hmac_alg[] = "hmac(sha1)"; +static const char hash_alg[] = "sha1"; + +struct sdesc { +	struct shash_desc shash; +	char ctx[]; +}; + +static struct crypto_shash *hashalg; +static struct crypto_shash *hmacalg; + +static struct sdesc *init_sdesc(struct crypto_shash *alg) +{ +	struct sdesc *sdesc; +	int size; + +	size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); +	sdesc = kmalloc(size, GFP_KERNEL); +	if (!sdesc) +		return ERR_PTR(-ENOMEM); +	sdesc->shash.tfm = alg; +	sdesc->shash.flags = 0x0; +	return sdesc; +} + +static int TSS_sha1(const unsigned char *data, unsigned int datalen, +		    unsigned char *digest) +{ +	struct sdesc *sdesc; +	int ret; + +	sdesc = init_sdesc(hashalg); +	if (IS_ERR(sdesc)) { +		pr_info("trusted_key: can't alloc %s\n", hash_alg); +		return PTR_ERR(sdesc); +	} + +	ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); +	kfree(sdesc); +	return ret; +} + +static int TSS_rawhmac(unsigned char *digest, const unsigned char *key, +		       unsigned int keylen, ...) +{ +	struct sdesc *sdesc; +	va_list argp; +	unsigned int dlen; +	unsigned char *data; +	int ret; + +	sdesc = init_sdesc(hmacalg); +	if (IS_ERR(sdesc)) { +		pr_info("trusted_key: can't alloc %s\n", hmac_alg); +		return PTR_ERR(sdesc); +	} + +	ret = crypto_shash_setkey(hmacalg, key, keylen); +	if (ret < 0) +		goto out; +	ret = crypto_shash_init(&sdesc->shash); +	if (ret < 0) +		goto out; + +	va_start(argp, keylen); +	for (;;) { +		dlen = va_arg(argp, unsigned int); +		if (dlen == 0) +			break; +		data = va_arg(argp, unsigned char *); +		if (data == NULL) { +			ret = -EINVAL; +			break; +		} +		ret = crypto_shash_update(&sdesc->shash, data, dlen); +		if (ret < 0) +			break; +	} +	va_end(argp); +	if (!ret) +		ret = crypto_shash_final(&sdesc->shash, digest); +out: +	kfree(sdesc); +	return ret; +} + +/* + * calculate authorization info fields to send to TPM + */ +static int TSS_authhmac(unsigned char *digest, const unsigned char *key, +			unsigned int keylen, unsigned char *h1, +			unsigned char *h2, unsigned char h3, ...) +{ +	unsigned char paramdigest[SHA1_DIGEST_SIZE]; +	struct sdesc *sdesc; +	unsigned int dlen; +	unsigned char *data; +	unsigned char c; +	int ret; +	va_list argp; + +	sdesc = init_sdesc(hashalg); +	if (IS_ERR(sdesc)) { +		pr_info("trusted_key: can't alloc %s\n", hash_alg); +		return PTR_ERR(sdesc); +	} + +	c = h3; +	ret = crypto_shash_init(&sdesc->shash); +	if (ret < 0) +		goto out; +	va_start(argp, h3); +	for (;;) { +		dlen = va_arg(argp, unsigned int); +		if (dlen == 0) +			break; +		data = va_arg(argp, unsigned char *); +		if (!data) { +			ret = -EINVAL; +			break; +		} +		ret = crypto_shash_update(&sdesc->shash, data, dlen); +		if (ret < 0) +			break; +	} +	va_end(argp); +	if (!ret) +		ret = crypto_shash_final(&sdesc->shash, paramdigest); +	if (!ret) +		ret = TSS_rawhmac(digest, key, keylen, SHA1_DIGEST_SIZE, +				  paramdigest, TPM_NONCE_SIZE, h1, +				  TPM_NONCE_SIZE, h2, 1, &c, 0, 0); +out: +	kfree(sdesc); +	return ret; +} + +/* + * verify the AUTH1_COMMAND (Seal) result from TPM + */ +static int TSS_checkhmac1(unsigned char *buffer, +			  const uint32_t command, +			  const unsigned char *ononce, +			  const unsigned char *key, +			  unsigned int keylen, ...) +{ +	uint32_t bufsize; +	uint16_t tag; +	uint32_t ordinal; +	uint32_t result; +	unsigned char *enonce; +	unsigned char *continueflag; +	unsigned char *authdata; +	unsigned char testhmac[SHA1_DIGEST_SIZE]; +	unsigned char paramdigest[SHA1_DIGEST_SIZE]; +	struct sdesc *sdesc; +	unsigned int dlen; +	unsigned int dpos; +	va_list argp; +	int ret; + +	bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); +	tag = LOAD16(buffer, 0); +	ordinal = command; +	result = LOAD32N(buffer, TPM_RETURN_OFFSET); +	if (tag == TPM_TAG_RSP_COMMAND) +		return 0; +	if (tag != TPM_TAG_RSP_AUTH1_COMMAND) +		return -EINVAL; +	authdata = buffer + bufsize - SHA1_DIGEST_SIZE; +	continueflag = authdata - 1; +	enonce = continueflag - TPM_NONCE_SIZE; + +	sdesc = init_sdesc(hashalg); +	if (IS_ERR(sdesc)) { +		pr_info("trusted_key: can't alloc %s\n", hash_alg); +		return PTR_ERR(sdesc); +	} +	ret = crypto_shash_init(&sdesc->shash); +	if (ret < 0) +		goto out; +	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, +				  sizeof result); +	if (ret < 0) +		goto out; +	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, +				  sizeof ordinal); +	if (ret < 0) +		goto out; +	va_start(argp, keylen); +	for (;;) { +		dlen = va_arg(argp, unsigned int); +		if (dlen == 0) +			break; +		dpos = va_arg(argp, unsigned int); +		ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); +		if (ret < 0) +			break; +	} +	va_end(argp); +	if (!ret) +		ret = crypto_shash_final(&sdesc->shash, paramdigest); +	if (ret < 0) +		goto out; + +	ret = TSS_rawhmac(testhmac, key, keylen, SHA1_DIGEST_SIZE, paramdigest, +			  TPM_NONCE_SIZE, enonce, TPM_NONCE_SIZE, ononce, +			  1, continueflag, 0, 0); +	if (ret < 0) +		goto out; + +	if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE)) +		ret = -EINVAL; +out: +	kfree(sdesc); +	return ret; +} + +/* + * verify the AUTH2_COMMAND (unseal) result from TPM + */ +static int TSS_checkhmac2(unsigned char *buffer, +			  const uint32_t command, +			  const unsigned char *ononce, +			  const unsigned char *key1, +			  unsigned int keylen1, +			  const unsigned char *key2, +			  unsigned int keylen2, ...) +{ +	uint32_t bufsize; +	uint16_t tag; +	uint32_t ordinal; +	uint32_t result; +	unsigned char *enonce1; +	unsigned char *continueflag1; +	unsigned char *authdata1; +	unsigned char *enonce2; +	unsigned char *continueflag2; +	unsigned char *authdata2; +	unsigned char testhmac1[SHA1_DIGEST_SIZE]; +	unsigned char testhmac2[SHA1_DIGEST_SIZE]; +	unsigned char paramdigest[SHA1_DIGEST_SIZE]; +	struct sdesc *sdesc; +	unsigned int dlen; +	unsigned int dpos; +	va_list argp; +	int ret; + +	bufsize = LOAD32(buffer, TPM_SIZE_OFFSET); +	tag = LOAD16(buffer, 0); +	ordinal = command; +	result = LOAD32N(buffer, TPM_RETURN_OFFSET); + +	if (tag == TPM_TAG_RSP_COMMAND) +		return 0; +	if (tag != TPM_TAG_RSP_AUTH2_COMMAND) +		return -EINVAL; +	authdata1 = buffer + bufsize - (SHA1_DIGEST_SIZE + 1 +			+ SHA1_DIGEST_SIZE + SHA1_DIGEST_SIZE); +	authdata2 = buffer + bufsize - (SHA1_DIGEST_SIZE); +	continueflag1 = authdata1 - 1; +	continueflag2 = authdata2 - 1; +	enonce1 = continueflag1 - TPM_NONCE_SIZE; +	enonce2 = continueflag2 - TPM_NONCE_SIZE; + +	sdesc = init_sdesc(hashalg); +	if (IS_ERR(sdesc)) { +		pr_info("trusted_key: can't alloc %s\n", hash_alg); +		return PTR_ERR(sdesc); +	} +	ret = crypto_shash_init(&sdesc->shash); +	if (ret < 0) +		goto out; +	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&result, +				  sizeof result); +	if (ret < 0) +		goto out; +	ret = crypto_shash_update(&sdesc->shash, (const u8 *)&ordinal, +				  sizeof ordinal); +	if (ret < 0) +		goto out; + +	va_start(argp, keylen2); +	for (;;) { +		dlen = va_arg(argp, unsigned int); +		if (dlen == 0) +			break; +		dpos = va_arg(argp, unsigned int); +		ret = crypto_shash_update(&sdesc->shash, buffer + dpos, dlen); +		if (ret < 0) +			break; +	} +	va_end(argp); +	if (!ret) +		ret = crypto_shash_final(&sdesc->shash, paramdigest); +	if (ret < 0) +		goto out; + +	ret = TSS_rawhmac(testhmac1, key1, keylen1, SHA1_DIGEST_SIZE, +			  paramdigest, TPM_NONCE_SIZE, enonce1, +			  TPM_NONCE_SIZE, ononce, 1, continueflag1, 0, 0); +	if (ret < 0) +		goto out; +	if (memcmp(testhmac1, authdata1, SHA1_DIGEST_SIZE)) { +		ret = -EINVAL; +		goto out; +	} +	ret = TSS_rawhmac(testhmac2, key2, keylen2, SHA1_DIGEST_SIZE, +			  paramdigest, TPM_NONCE_SIZE, enonce2, +			  TPM_NONCE_SIZE, ononce, 1, continueflag2, 0, 0); +	if (ret < 0) +		goto out; +	if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE)) +		ret = -EINVAL; +out: +	kfree(sdesc); +	return ret; +} + +/* + * For key specific tpm requests, we will generate and send our + * own TPM command packets using the drivers send function. + */ +static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd, +			    size_t buflen) +{ +	int rc; + +	dump_tpm_buf(cmd); +	rc = tpm_send(chip_num, cmd, buflen); +	dump_tpm_buf(cmd); +	if (rc > 0) +		/* Can't return positive return codes values to keyctl */ +		rc = -EPERM; +	return rc; +} + +/* + * Lock a trusted key, by extending a selected PCR. + * + * Prevents a trusted key that is sealed to PCRs from being accessed. + * This uses the tpm driver's extend function. + */ +static int pcrlock(const int pcrnum) +{ +	unsigned char hash[SHA1_DIGEST_SIZE]; +	int ret; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; +	ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE); +	if (ret != SHA1_DIGEST_SIZE) +		return ret; +	return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0; +} + +/* + * Create an object specific authorisation protocol (OSAP) session + */ +static int osap(struct tpm_buf *tb, struct osapsess *s, +		const unsigned char *key, uint16_t type, uint32_t handle) +{ +	unsigned char enonce[TPM_NONCE_SIZE]; +	unsigned char ononce[TPM_NONCE_SIZE]; +	int ret; + +	ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE); +	if (ret != TPM_NONCE_SIZE) +		return ret; + +	INIT_BUF(tb); +	store16(tb, TPM_TAG_RQU_COMMAND); +	store32(tb, TPM_OSAP_SIZE); +	store32(tb, TPM_ORD_OSAP); +	store16(tb, type); +	store32(tb, handle); +	storebytes(tb, ononce, TPM_NONCE_SIZE); + +	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); +	if (ret < 0) +		return ret; + +	s->handle = LOAD32(tb->data, TPM_DATA_OFFSET); +	memcpy(s->enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)]), +	       TPM_NONCE_SIZE); +	memcpy(enonce, &(tb->data[TPM_DATA_OFFSET + sizeof(uint32_t) + +				  TPM_NONCE_SIZE]), TPM_NONCE_SIZE); +	return TSS_rawhmac(s->secret, key, SHA1_DIGEST_SIZE, TPM_NONCE_SIZE, +			   enonce, TPM_NONCE_SIZE, ononce, 0, 0); +} + +/* + * Create an object independent authorisation protocol (oiap) session + */ +static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce) +{ +	int ret; + +	INIT_BUF(tb); +	store16(tb, TPM_TAG_RQU_COMMAND); +	store32(tb, TPM_OIAP_SIZE); +	store32(tb, TPM_ORD_OIAP); +	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); +	if (ret < 0) +		return ret; + +	*handle = LOAD32(tb->data, TPM_DATA_OFFSET); +	memcpy(nonce, &tb->data[TPM_DATA_OFFSET + sizeof(uint32_t)], +	       TPM_NONCE_SIZE); +	return 0; +} + +struct tpm_digests { +	unsigned char encauth[SHA1_DIGEST_SIZE]; +	unsigned char pubauth[SHA1_DIGEST_SIZE]; +	unsigned char xorwork[SHA1_DIGEST_SIZE * 2]; +	unsigned char xorhash[SHA1_DIGEST_SIZE]; +	unsigned char nonceodd[TPM_NONCE_SIZE]; +}; + +/* + * Have the TPM seal(encrypt) the trusted key, possibly based on + * Platform Configuration Registers (PCRs). AUTH1 for sealing key. + */ +static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, +		    uint32_t keyhandle, const unsigned char *keyauth, +		    const unsigned char *data, uint32_t datalen, +		    unsigned char *blob, uint32_t *bloblen, +		    const unsigned char *blobauth, +		    const unsigned char *pcrinfo, uint32_t pcrinfosize) +{ +	struct osapsess sess; +	struct tpm_digests *td; +	unsigned char cont; +	uint32_t ordinal; +	uint32_t pcrsize; +	uint32_t datsize; +	int sealinfosize; +	int encdatasize; +	int storedsize; +	int ret; +	int i; + +	/* alloc some work space for all the hashes */ +	td = kmalloc(sizeof *td, GFP_KERNEL); +	if (!td) +		return -ENOMEM; + +	/* get session for sealing key */ +	ret = osap(tb, &sess, keyauth, keytype, keyhandle); +	if (ret < 0) +		goto out; +	dump_sess(&sess); + +	/* calculate encrypted authorization value */ +	memcpy(td->xorwork, sess.secret, SHA1_DIGEST_SIZE); +	memcpy(td->xorwork + SHA1_DIGEST_SIZE, sess.enonce, SHA1_DIGEST_SIZE); +	ret = TSS_sha1(td->xorwork, SHA1_DIGEST_SIZE * 2, td->xorhash); +	if (ret < 0) +		goto out; + +	ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE); +	if (ret != TPM_NONCE_SIZE) +		goto out; +	ordinal = htonl(TPM_ORD_SEAL); +	datsize = htonl(datalen); +	pcrsize = htonl(pcrinfosize); +	cont = 0; + +	/* encrypt data authorization key */ +	for (i = 0; i < SHA1_DIGEST_SIZE; ++i) +		td->encauth[i] = td->xorhash[i] ^ blobauth[i]; + +	/* calculate authorization HMAC value */ +	if (pcrinfosize == 0) { +		/* no pcr info specified */ +		ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, +				   sess.enonce, td->nonceodd, cont, +				   sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, +				   td->encauth, sizeof(uint32_t), &pcrsize, +				   sizeof(uint32_t), &datsize, datalen, data, 0, +				   0); +	} else { +		/* pcr info specified */ +		ret = TSS_authhmac(td->pubauth, sess.secret, SHA1_DIGEST_SIZE, +				   sess.enonce, td->nonceodd, cont, +				   sizeof(uint32_t), &ordinal, SHA1_DIGEST_SIZE, +				   td->encauth, sizeof(uint32_t), &pcrsize, +				   pcrinfosize, pcrinfo, sizeof(uint32_t), +				   &datsize, datalen, data, 0, 0); +	} +	if (ret < 0) +		goto out; + +	/* build and send the TPM request packet */ +	INIT_BUF(tb); +	store16(tb, TPM_TAG_RQU_AUTH1_COMMAND); +	store32(tb, TPM_SEAL_SIZE + pcrinfosize + datalen); +	store32(tb, TPM_ORD_SEAL); +	store32(tb, keyhandle); +	storebytes(tb, td->encauth, SHA1_DIGEST_SIZE); +	store32(tb, pcrinfosize); +	storebytes(tb, pcrinfo, pcrinfosize); +	store32(tb, datalen); +	storebytes(tb, data, datalen); +	store32(tb, sess.handle); +	storebytes(tb, td->nonceodd, TPM_NONCE_SIZE); +	store8(tb, cont); +	storebytes(tb, td->pubauth, SHA1_DIGEST_SIZE); + +	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); +	if (ret < 0) +		goto out; + +	/* calculate the size of the returned Blob */ +	sealinfosize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t)); +	encdatasize = LOAD32(tb->data, TPM_DATA_OFFSET + sizeof(uint32_t) + +			     sizeof(uint32_t) + sealinfosize); +	storedsize = sizeof(uint32_t) + sizeof(uint32_t) + sealinfosize + +	    sizeof(uint32_t) + encdatasize; + +	/* check the HMAC in the response */ +	ret = TSS_checkhmac1(tb->data, ordinal, td->nonceodd, sess.secret, +			     SHA1_DIGEST_SIZE, storedsize, TPM_DATA_OFFSET, 0, +			     0); + +	/* copy the returned blob to caller */ +	if (!ret) { +		memcpy(blob, tb->data + TPM_DATA_OFFSET, storedsize); +		*bloblen = storedsize; +	} +out: +	kfree(td); +	return ret; +} + +/* + * use the AUTH2_COMMAND form of unseal, to authorize both key and blob + */ +static int tpm_unseal(struct tpm_buf *tb, +		      uint32_t keyhandle, const unsigned char *keyauth, +		      const unsigned char *blob, int bloblen, +		      const unsigned char *blobauth, +		      unsigned char *data, unsigned int *datalen) +{ +	unsigned char nonceodd[TPM_NONCE_SIZE]; +	unsigned char enonce1[TPM_NONCE_SIZE]; +	unsigned char enonce2[TPM_NONCE_SIZE]; +	unsigned char authdata1[SHA1_DIGEST_SIZE]; +	unsigned char authdata2[SHA1_DIGEST_SIZE]; +	uint32_t authhandle1 = 0; +	uint32_t authhandle2 = 0; +	unsigned char cont = 0; +	uint32_t ordinal; +	uint32_t keyhndl; +	int ret; + +	/* sessions for unsealing key and data */ +	ret = oiap(tb, &authhandle1, enonce1); +	if (ret < 0) { +		pr_info("trusted_key: oiap failed (%d)\n", ret); +		return ret; +	} +	ret = oiap(tb, &authhandle2, enonce2); +	if (ret < 0) { +		pr_info("trusted_key: oiap failed (%d)\n", ret); +		return ret; +	} + +	ordinal = htonl(TPM_ORD_UNSEAL); +	keyhndl = htonl(SRKHANDLE); +	ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE); +	if (ret != TPM_NONCE_SIZE) { +		pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); +		return ret; +	} +	ret = TSS_authhmac(authdata1, keyauth, TPM_NONCE_SIZE, +			   enonce1, nonceodd, cont, sizeof(uint32_t), +			   &ordinal, bloblen, blob, 0, 0); +	if (ret < 0) +		return ret; +	ret = TSS_authhmac(authdata2, blobauth, TPM_NONCE_SIZE, +			   enonce2, nonceodd, cont, sizeof(uint32_t), +			   &ordinal, bloblen, blob, 0, 0); +	if (ret < 0) +		return ret; + +	/* build and send TPM request packet */ +	INIT_BUF(tb); +	store16(tb, TPM_TAG_RQU_AUTH2_COMMAND); +	store32(tb, TPM_UNSEAL_SIZE + bloblen); +	store32(tb, TPM_ORD_UNSEAL); +	store32(tb, keyhandle); +	storebytes(tb, blob, bloblen); +	store32(tb, authhandle1); +	storebytes(tb, nonceodd, TPM_NONCE_SIZE); +	store8(tb, cont); +	storebytes(tb, authdata1, SHA1_DIGEST_SIZE); +	store32(tb, authhandle2); +	storebytes(tb, nonceodd, TPM_NONCE_SIZE); +	store8(tb, cont); +	storebytes(tb, authdata2, SHA1_DIGEST_SIZE); + +	ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, MAX_BUF_SIZE); +	if (ret < 0) { +		pr_info("trusted_key: authhmac failed (%d)\n", ret); +		return ret; +	} + +	*datalen = LOAD32(tb->data, TPM_DATA_OFFSET); +	ret = TSS_checkhmac2(tb->data, ordinal, nonceodd, +			     keyauth, SHA1_DIGEST_SIZE, +			     blobauth, SHA1_DIGEST_SIZE, +			     sizeof(uint32_t), TPM_DATA_OFFSET, +			     *datalen, TPM_DATA_OFFSET + sizeof(uint32_t), 0, +			     0); +	if (ret < 0) { +		pr_info("trusted_key: TSS_checkhmac2 failed (%d)\n", ret); +		return ret; +	} +	memcpy(data, tb->data + TPM_DATA_OFFSET + sizeof(uint32_t), *datalen); +	return 0; +} + +/* + * Have the TPM seal(encrypt) the symmetric key + */ +static int key_seal(struct trusted_key_payload *p, +		    struct trusted_key_options *o) +{ +	struct tpm_buf *tb; +	int ret; + +	tb = kzalloc(sizeof *tb, GFP_KERNEL); +	if (!tb) +		return -ENOMEM; + +	/* include migratable flag at end of sealed key */ +	p->key[p->key_len] = p->migratable; + +	ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth, +		       p->key, p->key_len + 1, p->blob, &p->blob_len, +		       o->blobauth, o->pcrinfo, o->pcrinfo_len); +	if (ret < 0) +		pr_info("trusted_key: srkseal failed (%d)\n", ret); + +	kfree(tb); +	return ret; +} + +/* + * Have the TPM unseal(decrypt) the symmetric key + */ +static int key_unseal(struct trusted_key_payload *p, +		      struct trusted_key_options *o) +{ +	struct tpm_buf *tb; +	int ret; + +	tb = kzalloc(sizeof *tb, GFP_KERNEL); +	if (!tb) +		return -ENOMEM; + +	ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len, +			 o->blobauth, p->key, &p->key_len); +	if (ret < 0) +		pr_info("trusted_key: srkunseal failed (%d)\n", ret); +	else +		/* pull migratable flag out of sealed key */ +		p->migratable = p->key[--p->key_len]; + +	kfree(tb); +	return ret; +} + +enum { +	Opt_err = -1, +	Opt_new, Opt_load, Opt_update, +	Opt_keyhandle, Opt_keyauth, Opt_blobauth, +	Opt_pcrinfo, Opt_pcrlock, Opt_migratable +}; + +static const match_table_t key_tokens = { +	{Opt_new, "new"}, +	{Opt_load, "load"}, +	{Opt_update, "update"}, +	{Opt_keyhandle, "keyhandle=%s"}, +	{Opt_keyauth, "keyauth=%s"}, +	{Opt_blobauth, "blobauth=%s"}, +	{Opt_pcrinfo, "pcrinfo=%s"}, +	{Opt_pcrlock, "pcrlock=%s"}, +	{Opt_migratable, "migratable=%s"}, +	{Opt_err, NULL} +}; + +/* can have zero or more token= options */ +static int getoptions(char *c, struct trusted_key_payload *pay, +		      struct trusted_key_options *opt) +{ +	substring_t args[MAX_OPT_ARGS]; +	char *p = c; +	int token; +	int res; +	unsigned long handle; +	unsigned long lock; + +	while ((p = strsep(&c, " \t"))) { +		if (*p == '\0' || *p == ' ' || *p == '\t') +			continue; +		token = match_token(p, key_tokens, args); + +		switch (token) { +		case Opt_pcrinfo: +			opt->pcrinfo_len = strlen(args[0].from) / 2; +			if (opt->pcrinfo_len > MAX_PCRINFO_SIZE) +				return -EINVAL; +			res = hex2bin(opt->pcrinfo, args[0].from, +				      opt->pcrinfo_len); +			if (res < 0) +				return -EINVAL; +			break; +		case Opt_keyhandle: +			res = kstrtoul(args[0].from, 16, &handle); +			if (res < 0) +				return -EINVAL; +			opt->keytype = SEAL_keytype; +			opt->keyhandle = handle; +			break; +		case Opt_keyauth: +			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) +				return -EINVAL; +			res = hex2bin(opt->keyauth, args[0].from, +				      SHA1_DIGEST_SIZE); +			if (res < 0) +				return -EINVAL; +			break; +		case Opt_blobauth: +			if (strlen(args[0].from) != 2 * SHA1_DIGEST_SIZE) +				return -EINVAL; +			res = hex2bin(opt->blobauth, args[0].from, +				      SHA1_DIGEST_SIZE); +			if (res < 0) +				return -EINVAL; +			break; +		case Opt_migratable: +			if (*args[0].from == '0') +				pay->migratable = 0; +			else +				return -EINVAL; +			break; +		case Opt_pcrlock: +			res = kstrtoul(args[0].from, 10, &lock); +			if (res < 0) +				return -EINVAL; +			opt->pcrlock = lock; +			break; +		default: +			return -EINVAL; +		} +	} +	return 0; +} + +/* + * datablob_parse - parse the keyctl data and fill in the + * 		    payload and options structures + * + * On success returns 0, otherwise -EINVAL. + */ +static int datablob_parse(char *datablob, struct trusted_key_payload *p, +			  struct trusted_key_options *o) +{ +	substring_t args[MAX_OPT_ARGS]; +	long keylen; +	int ret = -EINVAL; +	int key_cmd; +	char *c; + +	/* main command */ +	c = strsep(&datablob, " \t"); +	if (!c) +		return -EINVAL; +	key_cmd = match_token(c, key_tokens, args); +	switch (key_cmd) { +	case Opt_new: +		/* first argument is key size */ +		c = strsep(&datablob, " \t"); +		if (!c) +			return -EINVAL; +		ret = kstrtol(c, 10, &keylen); +		if (ret < 0 || keylen < MIN_KEY_SIZE || keylen > MAX_KEY_SIZE) +			return -EINVAL; +		p->key_len = keylen; +		ret = getoptions(datablob, p, o); +		if (ret < 0) +			return ret; +		ret = Opt_new; +		break; +	case Opt_load: +		/* first argument is sealed blob */ +		c = strsep(&datablob, " \t"); +		if (!c) +			return -EINVAL; +		p->blob_len = strlen(c) / 2; +		if (p->blob_len > MAX_BLOB_SIZE) +			return -EINVAL; +		ret = hex2bin(p->blob, c, p->blob_len); +		if (ret < 0) +			return -EINVAL; +		ret = getoptions(datablob, p, o); +		if (ret < 0) +			return ret; +		ret = Opt_load; +		break; +	case Opt_update: +		/* all arguments are options */ +		ret = getoptions(datablob, p, o); +		if (ret < 0) +			return ret; +		ret = Opt_update; +		break; +	case Opt_err: +		return -EINVAL; +		break; +	} +	return ret; +} + +static struct trusted_key_options *trusted_options_alloc(void) +{ +	struct trusted_key_options *options; + +	options = kzalloc(sizeof *options, GFP_KERNEL); +	if (options) { +		/* set any non-zero defaults */ +		options->keytype = SRK_keytype; +		options->keyhandle = SRKHANDLE; +	} +	return options; +} + +static struct trusted_key_payload *trusted_payload_alloc(struct key *key) +{ +	struct trusted_key_payload *p = NULL; +	int ret; + +	ret = key_payload_reserve(key, sizeof *p); +	if (ret < 0) +		return p; +	p = kzalloc(sizeof *p, GFP_KERNEL); +	if (p) +		p->migratable = 1; /* migratable by default */ +	return p; +} + +/* + * trusted_instantiate - create a new trusted key + * + * Unseal an existing trusted blob or, for a new key, get a + * random key, then seal and create a trusted key-type key, + * adding it to the specified keyring. + * + * On success, return 0. Otherwise return errno. + */ +static int trusted_instantiate(struct key *key, +			       struct key_preparsed_payload *prep) +{ +	struct trusted_key_payload *payload = NULL; +	struct trusted_key_options *options = NULL; +	size_t datalen = prep->datalen; +	char *datablob; +	int ret = 0; +	int key_cmd; +	size_t key_len; + +	if (datalen <= 0 || datalen > 32767 || !prep->data) +		return -EINVAL; + +	datablob = kmalloc(datalen + 1, GFP_KERNEL); +	if (!datablob) +		return -ENOMEM; +	memcpy(datablob, prep->data, datalen); +	datablob[datalen] = '\0'; + +	options = trusted_options_alloc(); +	if (!options) { +		ret = -ENOMEM; +		goto out; +	} +	payload = trusted_payload_alloc(key); +	if (!payload) { +		ret = -ENOMEM; +		goto out; +	} + +	key_cmd = datablob_parse(datablob, payload, options); +	if (key_cmd < 0) { +		ret = key_cmd; +		goto out; +	} + +	dump_payload(payload); +	dump_options(options); + +	switch (key_cmd) { +	case Opt_load: +		ret = key_unseal(payload, options); +		dump_payload(payload); +		dump_options(options); +		if (ret < 0) +			pr_info("trusted_key: key_unseal failed (%d)\n", ret); +		break; +	case Opt_new: +		key_len = payload->key_len; +		ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len); +		if (ret != key_len) { +			pr_info("trusted_key: key_create failed (%d)\n", ret); +			goto out; +		} +		ret = key_seal(payload, options); +		if (ret < 0) +			pr_info("trusted_key: key_seal failed (%d)\n", ret); +		break; +	default: +		ret = -EINVAL; +		goto out; +	} +	if (!ret && options->pcrlock) +		ret = pcrlock(options->pcrlock); +out: +	kfree(datablob); +	kfree(options); +	if (!ret) +		rcu_assign_keypointer(key, payload); +	else +		kfree(payload); +	return ret; +} + +static void trusted_rcu_free(struct rcu_head *rcu) +{ +	struct trusted_key_payload *p; + +	p = container_of(rcu, struct trusted_key_payload, rcu); +	memset(p->key, 0, p->key_len); +	kfree(p); +} + +/* + * trusted_update - reseal an existing key with new PCR values + */ +static int trusted_update(struct key *key, struct key_preparsed_payload *prep) +{ +	struct trusted_key_payload *p = key->payload.data; +	struct trusted_key_payload *new_p; +	struct trusted_key_options *new_o; +	size_t datalen = prep->datalen; +	char *datablob; +	int ret = 0; + +	if (!p->migratable) +		return -EPERM; +	if (datalen <= 0 || datalen > 32767 || !prep->data) +		return -EINVAL; + +	datablob = kmalloc(datalen + 1, GFP_KERNEL); +	if (!datablob) +		return -ENOMEM; +	new_o = trusted_options_alloc(); +	if (!new_o) { +		ret = -ENOMEM; +		goto out; +	} +	new_p = trusted_payload_alloc(key); +	if (!new_p) { +		ret = -ENOMEM; +		goto out; +	} + +	memcpy(datablob, prep->data, datalen); +	datablob[datalen] = '\0'; +	ret = datablob_parse(datablob, new_p, new_o); +	if (ret != Opt_update) { +		ret = -EINVAL; +		kfree(new_p); +		goto out; +	} +	/* copy old key values, and reseal with new pcrs */ +	new_p->migratable = p->migratable; +	new_p->key_len = p->key_len; +	memcpy(new_p->key, p->key, p->key_len); +	dump_payload(p); +	dump_payload(new_p); + +	ret = key_seal(new_p, new_o); +	if (ret < 0) { +		pr_info("trusted_key: key_seal failed (%d)\n", ret); +		kfree(new_p); +		goto out; +	} +	if (new_o->pcrlock) { +		ret = pcrlock(new_o->pcrlock); +		if (ret < 0) { +			pr_info("trusted_key: pcrlock failed (%d)\n", ret); +			kfree(new_p); +			goto out; +		} +	} +	rcu_assign_keypointer(key, new_p); +	call_rcu(&p->rcu, trusted_rcu_free); +out: +	kfree(datablob); +	kfree(new_o); +	return ret; +} + +/* + * trusted_read - copy the sealed blob data to userspace in hex. + * On success, return to userspace the trusted key datablob size. + */ +static long trusted_read(const struct key *key, char __user *buffer, +			 size_t buflen) +{ +	struct trusted_key_payload *p; +	char *ascii_buf; +	char *bufp; +	int i; + +	p = rcu_dereference_key(key); +	if (!p) +		return -EINVAL; +	if (!buffer || buflen <= 0) +		return 2 * p->blob_len; +	ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL); +	if (!ascii_buf) +		return -ENOMEM; + +	bufp = ascii_buf; +	for (i = 0; i < p->blob_len; i++) +		bufp = hex_byte_pack(bufp, p->blob[i]); +	if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) { +		kfree(ascii_buf); +		return -EFAULT; +	} +	kfree(ascii_buf); +	return 2 * p->blob_len; +} + +/* + * trusted_destroy - before freeing the key, clear the decrypted data + */ +static void trusted_destroy(struct key *key) +{ +	struct trusted_key_payload *p = key->payload.data; + +	if (!p) +		return; +	memset(p->key, 0, p->key_len); +	kfree(key->payload.data); +} + +struct key_type key_type_trusted = { +	.name = "trusted", +	.instantiate = trusted_instantiate, +	.update = trusted_update, +	.match = user_match, +	.destroy = trusted_destroy, +	.describe = user_describe, +	.read = trusted_read, +}; + +EXPORT_SYMBOL_GPL(key_type_trusted); + +static void trusted_shash_release(void) +{ +	if (hashalg) +		crypto_free_shash(hashalg); +	if (hmacalg) +		crypto_free_shash(hmacalg); +} + +static int __init trusted_shash_alloc(void) +{ +	int ret; + +	hmacalg = crypto_alloc_shash(hmac_alg, 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(hmacalg)) { +		pr_info("trusted_key: could not allocate crypto %s\n", +			hmac_alg); +		return PTR_ERR(hmacalg); +	} + +	hashalg = crypto_alloc_shash(hash_alg, 0, CRYPTO_ALG_ASYNC); +	if (IS_ERR(hashalg)) { +		pr_info("trusted_key: could not allocate crypto %s\n", +			hash_alg); +		ret = PTR_ERR(hashalg); +		goto hashalg_fail; +	} + +	return 0; + +hashalg_fail: +	crypto_free_shash(hmacalg); +	return ret; +} + +static int __init init_trusted(void) +{ +	int ret; + +	ret = trusted_shash_alloc(); +	if (ret < 0) +		return ret; +	ret = register_key_type(&key_type_trusted); +	if (ret < 0) +		trusted_shash_release(); +	return ret; +} + +static void __exit cleanup_trusted(void) +{ +	trusted_shash_release(); +	unregister_key_type(&key_type_trusted); +} + +late_initcall(init_trusted); +module_exit(cleanup_trusted); + +MODULE_LICENSE("GPL"); diff --git a/security/keys/trusted.h b/security/keys/trusted.h new file mode 100644 index 00000000000..3249fbd2b65 --- /dev/null +++ b/security/keys/trusted.h @@ -0,0 +1,134 @@ +#ifndef __TRUSTED_KEY_H +#define __TRUSTED_KEY_H + +/* implementation specific TPM constants */ +#define MAX_PCRINFO_SIZE		64 +#define MAX_BUF_SIZE			512 +#define TPM_GETRANDOM_SIZE		14 +#define TPM_OSAP_SIZE			36 +#define TPM_OIAP_SIZE			10 +#define TPM_SEAL_SIZE			87 +#define TPM_UNSEAL_SIZE			104 +#define TPM_SIZE_OFFSET			2 +#define TPM_RETURN_OFFSET		6 +#define TPM_DATA_OFFSET			10 + +#define LOAD32(buffer, offset)	(ntohl(*(uint32_t *)&buffer[offset])) +#define LOAD32N(buffer, offset)	(*(uint32_t *)&buffer[offset]) +#define LOAD16(buffer, offset)	(ntohs(*(uint16_t *)&buffer[offset])) + +struct tpm_buf { +	int len; +	unsigned char data[MAX_BUF_SIZE]; +}; + +#define INIT_BUF(tb) (tb->len = 0) + +struct osapsess { +	uint32_t handle; +	unsigned char secret[SHA1_DIGEST_SIZE]; +	unsigned char enonce[TPM_NONCE_SIZE]; +}; + +/* discrete values, but have to store in uint16_t for TPM use */ +enum { +	SEAL_keytype = 1, +	SRK_keytype = 4 +}; + +struct trusted_key_options { +	uint16_t keytype; +	uint32_t keyhandle; +	unsigned char keyauth[SHA1_DIGEST_SIZE]; +	unsigned char blobauth[SHA1_DIGEST_SIZE]; +	uint32_t pcrinfo_len; +	unsigned char pcrinfo[MAX_PCRINFO_SIZE]; +	int pcrlock; +}; + +#define TPM_DEBUG 0 + +#if TPM_DEBUG +static inline void dump_options(struct trusted_key_options *o) +{ +	pr_info("trusted_key: sealing key type %d\n", o->keytype); +	pr_info("trusted_key: sealing key handle %0X\n", o->keyhandle); +	pr_info("trusted_key: pcrlock %d\n", o->pcrlock); +	pr_info("trusted_key: pcrinfo %d\n", o->pcrinfo_len); +	print_hex_dump(KERN_INFO, "pcrinfo ", DUMP_PREFIX_NONE, +		       16, 1, o->pcrinfo, o->pcrinfo_len, 0); +} + +static inline void dump_payload(struct trusted_key_payload *p) +{ +	pr_info("trusted_key: key_len %d\n", p->key_len); +	print_hex_dump(KERN_INFO, "key ", DUMP_PREFIX_NONE, +		       16, 1, p->key, p->key_len, 0); +	pr_info("trusted_key: bloblen %d\n", p->blob_len); +	print_hex_dump(KERN_INFO, "blob ", DUMP_PREFIX_NONE, +		       16, 1, p->blob, p->blob_len, 0); +	pr_info("trusted_key: migratable %d\n", p->migratable); +} + +static inline void dump_sess(struct osapsess *s) +{ +	print_hex_dump(KERN_INFO, "trusted-key: handle ", DUMP_PREFIX_NONE, +		       16, 1, &s->handle, 4, 0); +	pr_info("trusted-key: secret:\n"); +	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, +		       16, 1, &s->secret, SHA1_DIGEST_SIZE, 0); +	pr_info("trusted-key: enonce:\n"); +	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, +		       16, 1, &s->enonce, SHA1_DIGEST_SIZE, 0); +} + +static inline void dump_tpm_buf(unsigned char *buf) +{ +	int len; + +	pr_info("\ntrusted-key: tpm buffer\n"); +	len = LOAD32(buf, TPM_SIZE_OFFSET); +	print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, buf, len, 0); +} +#else +static inline void dump_options(struct trusted_key_options *o) +{ +} + +static inline void dump_payload(struct trusted_key_payload *p) +{ +} + +static inline void dump_sess(struct osapsess *s) +{ +} + +static inline void dump_tpm_buf(unsigned char *buf) +{ +} +#endif + +static inline void store8(struct tpm_buf *buf, const unsigned char value) +{ +	buf->data[buf->len++] = value; +} + +static inline void store16(struct tpm_buf *buf, const uint16_t value) +{ +	*(uint16_t *) & buf->data[buf->len] = htons(value); +	buf->len += sizeof value; +} + +static inline void store32(struct tpm_buf *buf, const uint32_t value) +{ +	*(uint32_t *) & buf->data[buf->len] = htonl(value); +	buf->len += sizeof value; +} + +static inline void storebytes(struct tpm_buf *buf, const unsigned char *in, +			      const int len) +{ +	memcpy(buf->data + buf->len, in, len); +	buf->len += len; +} +#endif diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index e9aa0792965..faa2caeb593 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -18,34 +18,56 @@  #include <asm/uaccess.h>  #include "internal.h" +static int logon_vet_description(const char *desc); +  /*   * user defined keys take an arbitrary string as the description and an   * arbitrary blob of data as the payload   */  struct key_type key_type_user = { -	.name		= "user", -	.instantiate	= user_instantiate, -	.update		= user_update, -	.match		= user_match, -	.revoke		= user_revoke, -	.destroy	= user_destroy, -	.describe	= user_describe, -	.read		= user_read, +	.name			= "user", +	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT, +	.instantiate		= user_instantiate, +	.update			= user_update, +	.match			= user_match, +	.revoke			= user_revoke, +	.destroy		= user_destroy, +	.describe		= user_describe, +	.read			= user_read,  };  EXPORT_SYMBOL_GPL(key_type_user); -/*****************************************************************************/ +/* + * This key type is essentially the same as key_type_user, but it does + * not define a .read op. This is suitable for storing username and + * password pairs in the keyring that you do not want to be readable + * from userspace. + */ +struct key_type key_type_logon = { +	.name			= "logon", +	.def_lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT, +	.instantiate		= user_instantiate, +	.update			= user_update, +	.match			= user_match, +	.revoke			= user_revoke, +	.destroy		= user_destroy, +	.describe		= user_describe, +	.vet_description	= logon_vet_description, +}; +EXPORT_SYMBOL_GPL(key_type_logon); +  /*   * instantiate a user defined key   */ -int user_instantiate(struct key *key, const void *data, size_t datalen) +int user_instantiate(struct key *key, struct key_preparsed_payload *prep)  {  	struct user_key_payload *upayload; +	size_t datalen = prep->datalen;  	int ret;  	ret = -EINVAL; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		goto error;  	ret = key_payload_reserve(key, datalen); @@ -59,43 +81,28 @@ int user_instantiate(struct key *key, const void *data, size_t datalen)  	/* attach the data */  	upayload->datalen = datalen; -	memcpy(upayload->data, data, datalen); -	rcu_assign_pointer(key->payload.data, upayload); +	memcpy(upayload->data, prep->data, datalen); +	rcu_assign_keypointer(key, upayload);  	ret = 0;  error:  	return ret; - -} /* end user_instantiate() */ +}  EXPORT_SYMBOL_GPL(user_instantiate); -/*****************************************************************************/ -/* - * dispose of the old data from an updated user defined key - */ -static void user_update_rcu_disposal(struct rcu_head *rcu) -{ -	struct user_key_payload *upayload; - -	upayload = container_of(rcu, struct user_key_payload, rcu); - -	kfree(upayload); - -} /* end user_update_rcu_disposal() */ - -/*****************************************************************************/  /*   * update a user defined key   * - the key's semaphore is write-locked   */ -int user_update(struct key *key, const void *data, size_t datalen) +int user_update(struct key *key, struct key_preparsed_payload *prep)  {  	struct user_key_payload *upayload, *zap; +	size_t datalen = prep->datalen;  	int ret;  	ret = -EINVAL; -	if (datalen <= 0 || datalen > 32767 || !data) +	if (datalen <= 0 || datalen > 32767 || !prep->data)  		goto error;  	/* construct a replacement payload */ @@ -105,7 +112,7 @@ int user_update(struct key *key, const void *data, size_t datalen)  		goto error;  	upayload->datalen = datalen; -	memcpy(upayload->data, data, datalen); +	memcpy(upayload->data, prep->data, datalen);  	/* check the quota and attach the new data */  	zap = upayload; @@ -115,32 +122,29 @@ int user_update(struct key *key, const void *data, size_t datalen)  	if (ret == 0) {  		/* attach the new data, displacing the old */  		zap = key->payload.data; -		rcu_assign_pointer(key->payload.data, upayload); +		rcu_assign_keypointer(key, upayload);  		key->expiry = 0;  	} -	call_rcu(&zap->rcu, user_update_rcu_disposal); +	if (zap) +		kfree_rcu(zap, rcu);  error:  	return ret; - -} /* end user_update() */ +}  EXPORT_SYMBOL_GPL(user_update); -/*****************************************************************************/  /*   * match users on their name   */  int user_match(const struct key *key, const void *description)  {  	return strcmp(key->description, description) == 0; - -} /* end user_match() */ +}  EXPORT_SYMBOL_GPL(user_match); -/*****************************************************************************/  /*   * dispose of the links from a revoked keyring   * - called with the key sem write-locked @@ -153,15 +157,13 @@ void user_revoke(struct key *key)  	key_payload_reserve(key, 0);  	if (upayload) { -		rcu_assign_pointer(key->payload.data, NULL); -		call_rcu(&upayload->rcu, user_update_rcu_disposal); +		rcu_assign_keypointer(key, NULL); +		kfree_rcu(upayload, rcu);  	} - -} /* end user_revoke() */ +}  EXPORT_SYMBOL(user_revoke); -/*****************************************************************************/  /*   * dispose of the data dangling from the corpse of a user key   */ @@ -170,26 +172,22 @@ void user_destroy(struct key *key)  	struct user_key_payload *upayload = key->payload.data;  	kfree(upayload); - -} /* end user_destroy() */ +}  EXPORT_SYMBOL_GPL(user_destroy); -/*****************************************************************************/  /*   * describe the user key   */  void user_describe(const struct key *key, struct seq_file *m)  {  	seq_puts(m, key->description); - -	seq_printf(m, ": %u", key->datalen); - -} /* end user_describe() */ +	if (key_is_instantiated(key)) +		seq_printf(m, ": %u", key->datalen); +}  EXPORT_SYMBOL_GPL(user_describe); -/*****************************************************************************/  /*   * read the key data   * - the key's semaphore is read-locked @@ -199,8 +197,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)  	struct user_key_payload *upayload;  	long ret; -	upayload = rcu_dereference_protected( -		key->payload.data, rwsem_is_locked(&((struct key *)key)->sem)); +	upayload = rcu_dereference_key(key);  	ret = upayload->datalen;  	/* we can return the data as is */ @@ -213,7 +210,23 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)  	}  	return ret; - -} /* end user_read() */ +}  EXPORT_SYMBOL_GPL(user_read); + +/* Vet the description for a "logon" key */ +static int logon_vet_description(const char *desc) +{ +	char *p; + +	/* require a "qualified" description string */ +	p = strchr(desc, ':'); +	if (!p) +		return -EINVAL; + +	/* also reject description with ':' as first char */ +	if (p == desc) +		return -EINVAL; + +	return 0; +}  | 
