2038 lines · 49.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* Userspace key control operations * * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */ #include <linux/init.h>#include <linux/sched.h>#include <linux/sched/task.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>#include <linux/cred.h>#include <linux/string.h>#include <linux/err.h>#include <linux/vmalloc.h>#include <linux/security.h>#include <linux/uio.h>#include <linux/uaccess.h>#include <keys/request_key_auth-type.h>#include "internal.h" #define KEY_MAX_DESC_SIZE 4096 static const unsigned char keyrings_capabilities[2] = {	[0] = (KEYCTL_CAPS0_CAPABILITIES |	       (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS)	? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) |	       (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS)	? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) |	       (IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE)	? KEYCTL_CAPS0_PUBLIC_KEY : 0) |	       (IS_ENABLED(CONFIG_BIG_KEYS)		? KEYCTL_CAPS0_BIG_KEY : 0) |	       KEYCTL_CAPS0_INVALIDATE |	       KEYCTL_CAPS0_RESTRICT_KEYRING |	       KEYCTL_CAPS0_MOVE	       ),	[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |	       KEYCTL_CAPS1_NS_KEY_TAG |	       (IS_ENABLED(CONFIG_KEY_NOTIFICATIONS)	? KEYCTL_CAPS1_NOTIFICATIONS : 0)	       ),}; static int key_get_type_from_user(char *type,				  const char __user *_type,				  unsigned len){	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. * * 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,		const void __user *, _payload,		size_t, plen,		key_serial_t, ringid){	key_ref_t keyring_ref, key_ref;	char type[32], *description;	void *payload;	long ret; 	ret = -EINVAL;	if (plen > 1024 * 1024 - 1)		goto error; 	/* draw all the data into kernel space */	ret = key_get_type_from_user(type, _type, sizeof(type));	if (ret < 0)		goto error; 	description = NULL;	if (_description) {		description = strndup_user(_description, KEY_MAX_DESC_SIZE);		if (IS_ERR(description)) {			ret = PTR_ERR(description);			goto error;		}		if (!*description) {			kfree(description);			description = NULL;		} else if ((description[0] == '.') &&			   (strncmp(type, "keyring", 7) == 0)) {			ret = -EPERM;			goto error2;		}	} 	/* pull the payload in if one was supplied */	payload = NULL; 	if (plen) {		ret = -ENOMEM;		payload = kvmalloc(plen, GFP_KERNEL);		if (!payload)			goto error2; 		ret = -EFAULT;		if (copy_from_user(payload, _payload, plen) != 0)			goto error3;	} 	/* find the target keyring (which must be writable) */	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);	if (IS_ERR(keyring_ref)) {		ret = PTR_ERR(keyring_ref);		goto error3;	} 	/* create or update the requested key and add it to the target	 * keyring */	key_ref = key_create_or_update(keyring_ref, type, description,				       payload, plen, KEY_PERM_UNDEF,				       KEY_ALLOC_IN_QUOTA);	if (!IS_ERR(key_ref)) {		ret = key_ref_to_ptr(key_ref)->serial;		key_ref_put(key_ref);	}	else {		ret = PTR_ERR(key_ref);	} 	key_ref_put(keyring_ref); error3:	kvfree_sensitive(payload, plen); error2:	kfree(description); error:	return ret;} /* * 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,		const char __user *, _callout_info,		key_serial_t, destringid){	struct key_type *ktype;	struct key *key;	key_ref_t dest_ref;	size_t callout_len;	char type[32], *description, *callout_info;	long ret; 	/* pull the type into kernel space */	ret = key_get_type_from_user(type, _type, sizeof(type));	if (ret < 0)		goto error; 	/* pull the description into kernel space */	description = strndup_user(_description, KEY_MAX_DESC_SIZE);	if (IS_ERR(description)) {		ret = PTR_ERR(description);		goto error;	} 	/* pull the callout info into kernel space */	callout_info = NULL;	callout_len = 0;	if (_callout_info) {		callout_info = strndup_user(_callout_info, PAGE_SIZE);		if (IS_ERR(callout_info)) {			ret = PTR_ERR(callout_info);			goto error2;		}		callout_len = strlen(callout_info);	} 	/* get the destination keyring if specified */	dest_ref = NULL;	if (destringid) {		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,					   KEY_NEED_WRITE);		if (IS_ERR(dest_ref)) {			ret = PTR_ERR(dest_ref);			goto error3;		}	} 	/* find the key type */	ktype = key_type_lookup(type);	if (IS_ERR(ktype)) {		ret = PTR_ERR(ktype);		goto error4;	} 	/* do the search */	key = request_key_and_link(ktype, description, NULL, callout_info,				   callout_len, NULL, key_ref_to_ptr(dest_ref),				   KEY_ALLOC_IN_QUOTA);	if (IS_ERR(key)) {		ret = PTR_ERR(key);		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);error4:	key_ref_put(dest_ref);error3:	kfree(callout_info);error2:	kfree(description);error:	return ret;} /* * 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){	key_ref_t key_ref;	unsigned long lflags;	long ret; 	lflags = create ? KEY_LOOKUP_CREATE : 0;	key_ref = lookup_user_key(id, lflags, KEY_NEED_SEARCH);	if (IS_ERR(key_ref)) {		ret = PTR_ERR(key_ref);		goto error;	} 	ret = key_ref_to_ptr(key_ref)->serial;	key_ref_put(key_ref);error:	return ret;} /* * 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.  It is not permitted for userspace to create or join * keyrings whose name begin with a dot. * * If successful, the ID of the joined session keyring will be returned. */long keyctl_join_session_keyring(const char __user *_name){	char *name;	long ret; 	/* fetch the name from userspace */	name = NULL;	if (_name) {		name = strndup_user(_name, KEY_MAX_DESC_SIZE);		if (IS_ERR(name)) {			ret = PTR_ERR(name);			goto error;		} 		ret = -EPERM;		if (name[0] == '.')			goto error_name;	} 	/* join the session */	ret = join_session_keyring(name);error_name:	kfree(name);error:	return ret;} /* * 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,		       size_t plen){	key_ref_t key_ref;	void *payload;	long ret; 	ret = -EINVAL;	if (plen > PAGE_SIZE)		goto error; 	/* pull the payload in if one was supplied */	payload = NULL;	if (plen) {		ret = -ENOMEM;		payload = kvmalloc(plen, GFP_KERNEL);		if (!payload)			goto error; 		ret = -EFAULT;		if (copy_from_user(payload, _payload, plen) != 0)			goto error2;	} 	/* find the target key (which must be writable) */	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);	if (IS_ERR(key_ref)) {		ret = PTR_ERR(key_ref);		goto error2;	} 	/* update the key */	ret = key_update(key_ref, payload, plen); 	key_ref_put(key_ref);error2:	kvfree_sensitive(payload, plen);error:	return ret;} /* * 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). * * Keys with KEY_FLAG_KEEP set should not be revoked. * * If successful, 0 is returned. */long keyctl_revoke_key(key_serial_t id){	key_ref_t key_ref;	struct key *key;	long ret; 	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_NEED_SETATTR);		if (IS_ERR(key_ref)) {			ret = PTR_ERR(key_ref);			goto error;		}	} 	key = key_ref_to_ptr(key_ref);	ret = 0;	if (test_bit(KEY_FLAG_KEEP, &key->flags))		ret = -EPERM;	else		key_revoke(key); 	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. * * Keys with KEY_FLAG_KEEP set should not be invalidated. * * If successful, 0 is returned. */long keyctl_invalidate_key(key_serial_t id){	key_ref_t key_ref;	struct key *key;	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); 		/* Root is permitted to invalidate certain special keys */		if (capable(CAP_SYS_ADMIN)) {			key_ref = lookup_user_key(id, 0, KEY_SYSADMIN_OVERRIDE);			if (IS_ERR(key_ref))				goto error;			if (test_bit(KEY_FLAG_ROOT_CAN_INVAL,				     &key_ref_to_ptr(key_ref)->flags))				goto invalidate;			goto error_put;		} 		goto error;	} invalidate:	key = key_ref_to_ptr(key_ref);	ret = 0;	if (test_bit(KEY_FLAG_KEEP, &key->flags))		ret = -EPERM;	else		key_invalidate(key);error_put:	key_ref_put(key_ref);error:	kleave(" = %ld", ret);	return ret;} /* * 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 and not have * KEY_FLAG_KEEP set for this to work.  If successful, 0 will be returned. */long keyctl_keyring_clear(key_serial_t ringid){	key_ref_t keyring_ref;	struct key *keyring;	long ret; 	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,						      KEY_SYSADMIN_OVERRIDE);			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:	keyring = key_ref_to_ptr(keyring_ref);	if (test_bit(KEY_FLAG_KEEP, &keyring->flags))		ret = -EPERM;	else		ret = keyring_clear(keyring);error_put:	key_ref_put(keyring_ref);error:

Lines 1–500 of 2038.