162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Userspace key control operations
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/sched.h>
1062306a36Sopenharmony_ci#include <linux/sched/task.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/syscalls.h>
1362306a36Sopenharmony_ci#include <linux/key.h>
1462306a36Sopenharmony_ci#include <linux/keyctl.h>
1562306a36Sopenharmony_ci#include <linux/fs.h>
1662306a36Sopenharmony_ci#include <linux/capability.h>
1762306a36Sopenharmony_ci#include <linux/cred.h>
1862306a36Sopenharmony_ci#include <linux/string.h>
1962306a36Sopenharmony_ci#include <linux/err.h>
2062306a36Sopenharmony_ci#include <linux/vmalloc.h>
2162306a36Sopenharmony_ci#include <linux/security.h>
2262306a36Sopenharmony_ci#include <linux/uio.h>
2362306a36Sopenharmony_ci#include <linux/uaccess.h>
2462306a36Sopenharmony_ci#include <keys/request_key_auth-type.h>
2562306a36Sopenharmony_ci#include "internal.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define KEY_MAX_DESC_SIZE 4096
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const unsigned char keyrings_capabilities[2] = {
3062306a36Sopenharmony_ci	[0] = (KEYCTL_CAPS0_CAPABILITIES |
3162306a36Sopenharmony_ci	       (IS_ENABLED(CONFIG_PERSISTENT_KEYRINGS)	? KEYCTL_CAPS0_PERSISTENT_KEYRINGS : 0) |
3262306a36Sopenharmony_ci	       (IS_ENABLED(CONFIG_KEY_DH_OPERATIONS)	? KEYCTL_CAPS0_DIFFIE_HELLMAN : 0) |
3362306a36Sopenharmony_ci	       (IS_ENABLED(CONFIG_ASYMMETRIC_KEY_TYPE)	? KEYCTL_CAPS0_PUBLIC_KEY : 0) |
3462306a36Sopenharmony_ci	       (IS_ENABLED(CONFIG_BIG_KEYS)		? KEYCTL_CAPS0_BIG_KEY : 0) |
3562306a36Sopenharmony_ci	       KEYCTL_CAPS0_INVALIDATE |
3662306a36Sopenharmony_ci	       KEYCTL_CAPS0_RESTRICT_KEYRING |
3762306a36Sopenharmony_ci	       KEYCTL_CAPS0_MOVE
3862306a36Sopenharmony_ci	       ),
3962306a36Sopenharmony_ci	[1] = (KEYCTL_CAPS1_NS_KEYRING_NAME |
4062306a36Sopenharmony_ci	       KEYCTL_CAPS1_NS_KEY_TAG |
4162306a36Sopenharmony_ci	       (IS_ENABLED(CONFIG_KEY_NOTIFICATIONS)	? KEYCTL_CAPS1_NOTIFICATIONS : 0)
4262306a36Sopenharmony_ci	       ),
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int key_get_type_from_user(char *type,
4662306a36Sopenharmony_ci				  const char __user *_type,
4762306a36Sopenharmony_ci				  unsigned len)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int ret;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	ret = strncpy_from_user(type, _type, len);
5262306a36Sopenharmony_ci	if (ret < 0)
5362306a36Sopenharmony_ci		return ret;
5462306a36Sopenharmony_ci	if (ret == 0 || ret >= len)
5562306a36Sopenharmony_ci		return -EINVAL;
5662306a36Sopenharmony_ci	if (type[0] == '.')
5762306a36Sopenharmony_ci		return -EPERM;
5862306a36Sopenharmony_ci	type[len - 1] = '\0';
5962306a36Sopenharmony_ci	return 0;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*
6362306a36Sopenharmony_ci * Extract the description of a new key from userspace and either add it as a
6462306a36Sopenharmony_ci * new key to the specified keyring or update a matching key in that keyring.
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * If the description is NULL or an empty string, the key type is asked to
6762306a36Sopenharmony_ci * generate one from the payload.
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * The keyring must be writable so that we can attach the key to it.
7062306a36Sopenharmony_ci *
7162306a36Sopenharmony_ci * If successful, the new key's serial number is returned, otherwise an error
7262306a36Sopenharmony_ci * code is returned.
7362306a36Sopenharmony_ci */
7462306a36Sopenharmony_ciSYSCALL_DEFINE5(add_key, const char __user *, _type,
7562306a36Sopenharmony_ci		const char __user *, _description,
7662306a36Sopenharmony_ci		const void __user *, _payload,
7762306a36Sopenharmony_ci		size_t, plen,
7862306a36Sopenharmony_ci		key_serial_t, ringid)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	key_ref_t keyring_ref, key_ref;
8162306a36Sopenharmony_ci	char type[32], *description;
8262306a36Sopenharmony_ci	void *payload;
8362306a36Sopenharmony_ci	long ret;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ret = -EINVAL;
8662306a36Sopenharmony_ci	if (plen > 1024 * 1024 - 1)
8762306a36Sopenharmony_ci		goto error;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* draw all the data into kernel space */
9062306a36Sopenharmony_ci	ret = key_get_type_from_user(type, _type, sizeof(type));
9162306a36Sopenharmony_ci	if (ret < 0)
9262306a36Sopenharmony_ci		goto error;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	description = NULL;
9562306a36Sopenharmony_ci	if (_description) {
9662306a36Sopenharmony_ci		description = strndup_user(_description, KEY_MAX_DESC_SIZE);
9762306a36Sopenharmony_ci		if (IS_ERR(description)) {
9862306a36Sopenharmony_ci			ret = PTR_ERR(description);
9962306a36Sopenharmony_ci			goto error;
10062306a36Sopenharmony_ci		}
10162306a36Sopenharmony_ci		if (!*description) {
10262306a36Sopenharmony_ci			kfree(description);
10362306a36Sopenharmony_ci			description = NULL;
10462306a36Sopenharmony_ci		} else if ((description[0] == '.') &&
10562306a36Sopenharmony_ci			   (strncmp(type, "keyring", 7) == 0)) {
10662306a36Sopenharmony_ci			ret = -EPERM;
10762306a36Sopenharmony_ci			goto error2;
10862306a36Sopenharmony_ci		}
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* pull the payload in if one was supplied */
11262306a36Sopenharmony_ci	payload = NULL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (plen) {
11562306a36Sopenharmony_ci		ret = -ENOMEM;
11662306a36Sopenharmony_ci		payload = kvmalloc(plen, GFP_KERNEL);
11762306a36Sopenharmony_ci		if (!payload)
11862306a36Sopenharmony_ci			goto error2;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		ret = -EFAULT;
12162306a36Sopenharmony_ci		if (copy_from_user(payload, _payload, plen) != 0)
12262306a36Sopenharmony_ci			goto error3;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* find the target keyring (which must be writable) */
12662306a36Sopenharmony_ci	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
12762306a36Sopenharmony_ci	if (IS_ERR(keyring_ref)) {
12862306a36Sopenharmony_ci		ret = PTR_ERR(keyring_ref);
12962306a36Sopenharmony_ci		goto error3;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* create or update the requested key and add it to the target
13362306a36Sopenharmony_ci	 * keyring */
13462306a36Sopenharmony_ci	key_ref = key_create_or_update(keyring_ref, type, description,
13562306a36Sopenharmony_ci				       payload, plen, KEY_PERM_UNDEF,
13662306a36Sopenharmony_ci				       KEY_ALLOC_IN_QUOTA);
13762306a36Sopenharmony_ci	if (!IS_ERR(key_ref)) {
13862306a36Sopenharmony_ci		ret = key_ref_to_ptr(key_ref)->serial;
13962306a36Sopenharmony_ci		key_ref_put(key_ref);
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci	else {
14262306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	key_ref_put(keyring_ref);
14662306a36Sopenharmony_ci error3:
14762306a36Sopenharmony_ci	kvfree_sensitive(payload, plen);
14862306a36Sopenharmony_ci error2:
14962306a36Sopenharmony_ci	kfree(description);
15062306a36Sopenharmony_ci error:
15162306a36Sopenharmony_ci	return ret;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Search the process keyrings and keyring trees linked from those for a
15662306a36Sopenharmony_ci * matching key.  Keyrings must have appropriate Search permission to be
15762306a36Sopenharmony_ci * searched.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * If a key is found, it will be attached to the destination keyring if there's
16062306a36Sopenharmony_ci * one specified and the serial number of the key will be returned.
16162306a36Sopenharmony_ci *
16262306a36Sopenharmony_ci * If no key is found, /sbin/request-key will be invoked if _callout_info is
16362306a36Sopenharmony_ci * non-NULL in an attempt to create a key.  The _callout_info string will be
16462306a36Sopenharmony_ci * passed to /sbin/request-key to aid with completing the request.  If the
16562306a36Sopenharmony_ci * _callout_info string is "" then it will be changed to "-".
16662306a36Sopenharmony_ci */
16762306a36Sopenharmony_ciSYSCALL_DEFINE4(request_key, const char __user *, _type,
16862306a36Sopenharmony_ci		const char __user *, _description,
16962306a36Sopenharmony_ci		const char __user *, _callout_info,
17062306a36Sopenharmony_ci		key_serial_t, destringid)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct key_type *ktype;
17362306a36Sopenharmony_ci	struct key *key;
17462306a36Sopenharmony_ci	key_ref_t dest_ref;
17562306a36Sopenharmony_ci	size_t callout_len;
17662306a36Sopenharmony_ci	char type[32], *description, *callout_info;
17762306a36Sopenharmony_ci	long ret;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* pull the type into kernel space */
18062306a36Sopenharmony_ci	ret = key_get_type_from_user(type, _type, sizeof(type));
18162306a36Sopenharmony_ci	if (ret < 0)
18262306a36Sopenharmony_ci		goto error;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	/* pull the description into kernel space */
18562306a36Sopenharmony_ci	description = strndup_user(_description, KEY_MAX_DESC_SIZE);
18662306a36Sopenharmony_ci	if (IS_ERR(description)) {
18762306a36Sopenharmony_ci		ret = PTR_ERR(description);
18862306a36Sopenharmony_ci		goto error;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	/* pull the callout info into kernel space */
19262306a36Sopenharmony_ci	callout_info = NULL;
19362306a36Sopenharmony_ci	callout_len = 0;
19462306a36Sopenharmony_ci	if (_callout_info) {
19562306a36Sopenharmony_ci		callout_info = strndup_user(_callout_info, PAGE_SIZE);
19662306a36Sopenharmony_ci		if (IS_ERR(callout_info)) {
19762306a36Sopenharmony_ci			ret = PTR_ERR(callout_info);
19862306a36Sopenharmony_ci			goto error2;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci		callout_len = strlen(callout_info);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* get the destination keyring if specified */
20462306a36Sopenharmony_ci	dest_ref = NULL;
20562306a36Sopenharmony_ci	if (destringid) {
20662306a36Sopenharmony_ci		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
20762306a36Sopenharmony_ci					   KEY_NEED_WRITE);
20862306a36Sopenharmony_ci		if (IS_ERR(dest_ref)) {
20962306a36Sopenharmony_ci			ret = PTR_ERR(dest_ref);
21062306a36Sopenharmony_ci			goto error3;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* find the key type */
21562306a36Sopenharmony_ci	ktype = key_type_lookup(type);
21662306a36Sopenharmony_ci	if (IS_ERR(ktype)) {
21762306a36Sopenharmony_ci		ret = PTR_ERR(ktype);
21862306a36Sopenharmony_ci		goto error4;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* do the search */
22262306a36Sopenharmony_ci	key = request_key_and_link(ktype, description, NULL, callout_info,
22362306a36Sopenharmony_ci				   callout_len, NULL, key_ref_to_ptr(dest_ref),
22462306a36Sopenharmony_ci				   KEY_ALLOC_IN_QUOTA);
22562306a36Sopenharmony_ci	if (IS_ERR(key)) {
22662306a36Sopenharmony_ci		ret = PTR_ERR(key);
22762306a36Sopenharmony_ci		goto error5;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	/* wait for the key to finish being constructed */
23162306a36Sopenharmony_ci	ret = wait_for_key_construction(key, 1);
23262306a36Sopenharmony_ci	if (ret < 0)
23362306a36Sopenharmony_ci		goto error6;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	ret = key->serial;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cierror6:
23862306a36Sopenharmony_ci 	key_put(key);
23962306a36Sopenharmony_cierror5:
24062306a36Sopenharmony_ci	key_type_put(ktype);
24162306a36Sopenharmony_cierror4:
24262306a36Sopenharmony_ci	key_ref_put(dest_ref);
24362306a36Sopenharmony_cierror3:
24462306a36Sopenharmony_ci	kfree(callout_info);
24562306a36Sopenharmony_cierror2:
24662306a36Sopenharmony_ci	kfree(description);
24762306a36Sopenharmony_cierror:
24862306a36Sopenharmony_ci	return ret;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/*
25262306a36Sopenharmony_ci * Get the ID of the specified process keyring.
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci * The requested keyring must have search permission to be found.
25562306a36Sopenharmony_ci *
25662306a36Sopenharmony_ci * If successful, the ID of the requested keyring will be returned.
25762306a36Sopenharmony_ci */
25862306a36Sopenharmony_cilong keyctl_get_keyring_ID(key_serial_t id, int create)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	key_ref_t key_ref;
26162306a36Sopenharmony_ci	unsigned long lflags;
26262306a36Sopenharmony_ci	long ret;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	lflags = create ? KEY_LOOKUP_CREATE : 0;
26562306a36Sopenharmony_ci	key_ref = lookup_user_key(id, lflags, KEY_NEED_SEARCH);
26662306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
26762306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
26862306a36Sopenharmony_ci		goto error;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	ret = key_ref_to_ptr(key_ref)->serial;
27262306a36Sopenharmony_ci	key_ref_put(key_ref);
27362306a36Sopenharmony_cierror:
27462306a36Sopenharmony_ci	return ret;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci/*
27862306a36Sopenharmony_ci * Join a (named) session keyring.
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Create and join an anonymous session keyring or join a named session
28162306a36Sopenharmony_ci * keyring, creating it if necessary.  A named session keyring must have Search
28262306a36Sopenharmony_ci * permission for it to be joined.  Session keyrings without this permit will
28362306a36Sopenharmony_ci * be skipped over.  It is not permitted for userspace to create or join
28462306a36Sopenharmony_ci * keyrings whose name begin with a dot.
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * If successful, the ID of the joined session keyring will be returned.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cilong keyctl_join_session_keyring(const char __user *_name)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	char *name;
29162306a36Sopenharmony_ci	long ret;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/* fetch the name from userspace */
29462306a36Sopenharmony_ci	name = NULL;
29562306a36Sopenharmony_ci	if (_name) {
29662306a36Sopenharmony_ci		name = strndup_user(_name, KEY_MAX_DESC_SIZE);
29762306a36Sopenharmony_ci		if (IS_ERR(name)) {
29862306a36Sopenharmony_ci			ret = PTR_ERR(name);
29962306a36Sopenharmony_ci			goto error;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		ret = -EPERM;
30362306a36Sopenharmony_ci		if (name[0] == '.')
30462306a36Sopenharmony_ci			goto error_name;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* join the session */
30862306a36Sopenharmony_ci	ret = join_session_keyring(name);
30962306a36Sopenharmony_cierror_name:
31062306a36Sopenharmony_ci	kfree(name);
31162306a36Sopenharmony_cierror:
31262306a36Sopenharmony_ci	return ret;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/*
31662306a36Sopenharmony_ci * Update a key's data payload from the given data.
31762306a36Sopenharmony_ci *
31862306a36Sopenharmony_ci * The key must grant the caller Write permission and the key type must support
31962306a36Sopenharmony_ci * updating for this to work.  A negative key can be positively instantiated
32062306a36Sopenharmony_ci * with this call.
32162306a36Sopenharmony_ci *
32262306a36Sopenharmony_ci * If successful, 0 will be returned.  If the key type does not support
32362306a36Sopenharmony_ci * updating, then -EOPNOTSUPP will be returned.
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_cilong keyctl_update_key(key_serial_t id,
32662306a36Sopenharmony_ci		       const void __user *_payload,
32762306a36Sopenharmony_ci		       size_t plen)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	key_ref_t key_ref;
33062306a36Sopenharmony_ci	void *payload;
33162306a36Sopenharmony_ci	long ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ret = -EINVAL;
33462306a36Sopenharmony_ci	if (plen > PAGE_SIZE)
33562306a36Sopenharmony_ci		goto error;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* pull the payload in if one was supplied */
33862306a36Sopenharmony_ci	payload = NULL;
33962306a36Sopenharmony_ci	if (plen) {
34062306a36Sopenharmony_ci		ret = -ENOMEM;
34162306a36Sopenharmony_ci		payload = kvmalloc(plen, GFP_KERNEL);
34262306a36Sopenharmony_ci		if (!payload)
34362306a36Sopenharmony_ci			goto error;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		ret = -EFAULT;
34662306a36Sopenharmony_ci		if (copy_from_user(payload, _payload, plen) != 0)
34762306a36Sopenharmony_ci			goto error2;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* find the target key (which must be writable) */
35162306a36Sopenharmony_ci	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);
35262306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
35362306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
35462306a36Sopenharmony_ci		goto error2;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* update the key */
35862306a36Sopenharmony_ci	ret = key_update(key_ref, payload, plen);
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	key_ref_put(key_ref);
36162306a36Sopenharmony_cierror2:
36262306a36Sopenharmony_ci	kvfree_sensitive(payload, plen);
36362306a36Sopenharmony_cierror:
36462306a36Sopenharmony_ci	return ret;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/*
36862306a36Sopenharmony_ci * Revoke a key.
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * The key must be grant the caller Write or Setattr permission for this to
37162306a36Sopenharmony_ci * work.  The key type should give up its quota claim when revoked.  The key
37262306a36Sopenharmony_ci * and any links to the key will be automatically garbage collected after a
37362306a36Sopenharmony_ci * certain amount of time (/proc/sys/kernel/keys/gc_delay).
37462306a36Sopenharmony_ci *
37562306a36Sopenharmony_ci * Keys with KEY_FLAG_KEEP set should not be revoked.
37662306a36Sopenharmony_ci *
37762306a36Sopenharmony_ci * If successful, 0 is returned.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cilong keyctl_revoke_key(key_serial_t id)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	key_ref_t key_ref;
38262306a36Sopenharmony_ci	struct key *key;
38362306a36Sopenharmony_ci	long ret;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	key_ref = lookup_user_key(id, 0, KEY_NEED_WRITE);
38662306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
38762306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
38862306a36Sopenharmony_ci		if (ret != -EACCES)
38962306a36Sopenharmony_ci			goto error;
39062306a36Sopenharmony_ci		key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
39162306a36Sopenharmony_ci		if (IS_ERR(key_ref)) {
39262306a36Sopenharmony_ci			ret = PTR_ERR(key_ref);
39362306a36Sopenharmony_ci			goto error;
39462306a36Sopenharmony_ci		}
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
39862306a36Sopenharmony_ci	ret = 0;
39962306a36Sopenharmony_ci	if (test_bit(KEY_FLAG_KEEP, &key->flags))
40062306a36Sopenharmony_ci		ret = -EPERM;
40162306a36Sopenharmony_ci	else
40262306a36Sopenharmony_ci		key_revoke(key);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	key_ref_put(key_ref);
40562306a36Sopenharmony_cierror:
40662306a36Sopenharmony_ci	return ret;
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci/*
41062306a36Sopenharmony_ci * Invalidate a key.
41162306a36Sopenharmony_ci *
41262306a36Sopenharmony_ci * The key must be grant the caller Invalidate permission for this to work.
41362306a36Sopenharmony_ci * The key and any links to the key will be automatically garbage collected
41462306a36Sopenharmony_ci * immediately.
41562306a36Sopenharmony_ci *
41662306a36Sopenharmony_ci * Keys with KEY_FLAG_KEEP set should not be invalidated.
41762306a36Sopenharmony_ci *
41862306a36Sopenharmony_ci * If successful, 0 is returned.
41962306a36Sopenharmony_ci */
42062306a36Sopenharmony_cilong keyctl_invalidate_key(key_serial_t id)
42162306a36Sopenharmony_ci{
42262306a36Sopenharmony_ci	key_ref_t key_ref;
42362306a36Sopenharmony_ci	struct key *key;
42462306a36Sopenharmony_ci	long ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	kenter("%d", id);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH);
42962306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
43062306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		/* Root is permitted to invalidate certain special keys */
43362306a36Sopenharmony_ci		if (capable(CAP_SYS_ADMIN)) {
43462306a36Sopenharmony_ci			key_ref = lookup_user_key(id, 0, KEY_SYSADMIN_OVERRIDE);
43562306a36Sopenharmony_ci			if (IS_ERR(key_ref))
43662306a36Sopenharmony_ci				goto error;
43762306a36Sopenharmony_ci			if (test_bit(KEY_FLAG_ROOT_CAN_INVAL,
43862306a36Sopenharmony_ci				     &key_ref_to_ptr(key_ref)->flags))
43962306a36Sopenharmony_ci				goto invalidate;
44062306a36Sopenharmony_ci			goto error_put;
44162306a36Sopenharmony_ci		}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		goto error;
44462306a36Sopenharmony_ci	}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ciinvalidate:
44762306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
44862306a36Sopenharmony_ci	ret = 0;
44962306a36Sopenharmony_ci	if (test_bit(KEY_FLAG_KEEP, &key->flags))
45062306a36Sopenharmony_ci		ret = -EPERM;
45162306a36Sopenharmony_ci	else
45262306a36Sopenharmony_ci		key_invalidate(key);
45362306a36Sopenharmony_cierror_put:
45462306a36Sopenharmony_ci	key_ref_put(key_ref);
45562306a36Sopenharmony_cierror:
45662306a36Sopenharmony_ci	kleave(" = %ld", ret);
45762306a36Sopenharmony_ci	return ret;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/*
46162306a36Sopenharmony_ci * Clear the specified keyring, creating an empty process keyring if one of the
46262306a36Sopenharmony_ci * special keyring IDs is used.
46362306a36Sopenharmony_ci *
46462306a36Sopenharmony_ci * The keyring must grant the caller Write permission and not have
46562306a36Sopenharmony_ci * KEY_FLAG_KEEP set for this to work.  If successful, 0 will be returned.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cilong keyctl_keyring_clear(key_serial_t ringid)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	key_ref_t keyring_ref;
47062306a36Sopenharmony_ci	struct key *keyring;
47162306a36Sopenharmony_ci	long ret;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
47462306a36Sopenharmony_ci	if (IS_ERR(keyring_ref)) {
47562306a36Sopenharmony_ci		ret = PTR_ERR(keyring_ref);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		/* Root is permitted to invalidate certain special keyrings */
47862306a36Sopenharmony_ci		if (capable(CAP_SYS_ADMIN)) {
47962306a36Sopenharmony_ci			keyring_ref = lookup_user_key(ringid, 0,
48062306a36Sopenharmony_ci						      KEY_SYSADMIN_OVERRIDE);
48162306a36Sopenharmony_ci			if (IS_ERR(keyring_ref))
48262306a36Sopenharmony_ci				goto error;
48362306a36Sopenharmony_ci			if (test_bit(KEY_FLAG_ROOT_CAN_CLEAR,
48462306a36Sopenharmony_ci				     &key_ref_to_ptr(keyring_ref)->flags))
48562306a36Sopenharmony_ci				goto clear;
48662306a36Sopenharmony_ci			goto error_put;
48762306a36Sopenharmony_ci		}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		goto error;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ciclear:
49362306a36Sopenharmony_ci	keyring = key_ref_to_ptr(keyring_ref);
49462306a36Sopenharmony_ci	if (test_bit(KEY_FLAG_KEEP, &keyring->flags))
49562306a36Sopenharmony_ci		ret = -EPERM;
49662306a36Sopenharmony_ci	else
49762306a36Sopenharmony_ci		ret = keyring_clear(keyring);
49862306a36Sopenharmony_cierror_put:
49962306a36Sopenharmony_ci	key_ref_put(keyring_ref);
50062306a36Sopenharmony_cierror:
50162306a36Sopenharmony_ci	return ret;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/*
50562306a36Sopenharmony_ci * Create a link from a keyring to a key if there's no matching key in the
50662306a36Sopenharmony_ci * keyring, otherwise replace the link to the matching key with a link to the
50762306a36Sopenharmony_ci * new key.
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * The key must grant the caller Link permission and the keyring must grant
51062306a36Sopenharmony_ci * the caller Write permission.  Furthermore, if an additional link is created,
51162306a36Sopenharmony_ci * the keyring's quota will be extended.
51262306a36Sopenharmony_ci *
51362306a36Sopenharmony_ci * If successful, 0 will be returned.
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cilong keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	key_ref_t keyring_ref, key_ref;
51862306a36Sopenharmony_ci	long ret;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
52162306a36Sopenharmony_ci	if (IS_ERR(keyring_ref)) {
52262306a36Sopenharmony_ci		ret = PTR_ERR(keyring_ref);
52362306a36Sopenharmony_ci		goto error;
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);
52762306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
52862306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
52962306a36Sopenharmony_ci		goto error2;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	ret = key_link(key_ref_to_ptr(keyring_ref), key_ref_to_ptr(key_ref));
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	key_ref_put(key_ref);
53562306a36Sopenharmony_cierror2:
53662306a36Sopenharmony_ci	key_ref_put(keyring_ref);
53762306a36Sopenharmony_cierror:
53862306a36Sopenharmony_ci	return ret;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci/*
54262306a36Sopenharmony_ci * Unlink a key from a keyring.
54362306a36Sopenharmony_ci *
54462306a36Sopenharmony_ci * The keyring must grant the caller Write permission for this to work; the key
54562306a36Sopenharmony_ci * itself need not grant the caller anything.  If the last link to a key is
54662306a36Sopenharmony_ci * removed then that key will be scheduled for destruction.
54762306a36Sopenharmony_ci *
54862306a36Sopenharmony_ci * Keys or keyrings with KEY_FLAG_KEEP set should not be unlinked.
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * If successful, 0 will be returned.
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_cilong keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	key_ref_t keyring_ref, key_ref;
55562306a36Sopenharmony_ci	struct key *keyring, *key;
55662306a36Sopenharmony_ci	long ret;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_WRITE);
55962306a36Sopenharmony_ci	if (IS_ERR(keyring_ref)) {
56062306a36Sopenharmony_ci		ret = PTR_ERR(keyring_ref);
56162306a36Sopenharmony_ci		goto error;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_PARTIAL, KEY_NEED_UNLINK);
56562306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
56662306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
56762306a36Sopenharmony_ci		goto error2;
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	keyring = key_ref_to_ptr(keyring_ref);
57162306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
57262306a36Sopenharmony_ci	if (test_bit(KEY_FLAG_KEEP, &keyring->flags) &&
57362306a36Sopenharmony_ci	    test_bit(KEY_FLAG_KEEP, &key->flags))
57462306a36Sopenharmony_ci		ret = -EPERM;
57562306a36Sopenharmony_ci	else
57662306a36Sopenharmony_ci		ret = key_unlink(keyring, key);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	key_ref_put(key_ref);
57962306a36Sopenharmony_cierror2:
58062306a36Sopenharmony_ci	key_ref_put(keyring_ref);
58162306a36Sopenharmony_cierror:
58262306a36Sopenharmony_ci	return ret;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/*
58662306a36Sopenharmony_ci * Move a link to a key from one keyring to another, displacing any matching
58762306a36Sopenharmony_ci * key from the destination keyring.
58862306a36Sopenharmony_ci *
58962306a36Sopenharmony_ci * The key must grant the caller Link permission and both keyrings must grant
59062306a36Sopenharmony_ci * the caller Write permission.  There must also be a link in the from keyring
59162306a36Sopenharmony_ci * to the key.  If both keyrings are the same, nothing is done.
59262306a36Sopenharmony_ci *
59362306a36Sopenharmony_ci * If successful, 0 will be returned.
59462306a36Sopenharmony_ci */
59562306a36Sopenharmony_cilong keyctl_keyring_move(key_serial_t id, key_serial_t from_ringid,
59662306a36Sopenharmony_ci			 key_serial_t to_ringid, unsigned int flags)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	key_ref_t key_ref, from_ref, to_ref;
59962306a36Sopenharmony_ci	long ret;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	if (flags & ~KEYCTL_MOVE_EXCL)
60262306a36Sopenharmony_ci		return -EINVAL;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_LINK);
60562306a36Sopenharmony_ci	if (IS_ERR(key_ref))
60662306a36Sopenharmony_ci		return PTR_ERR(key_ref);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	from_ref = lookup_user_key(from_ringid, 0, KEY_NEED_WRITE);
60962306a36Sopenharmony_ci	if (IS_ERR(from_ref)) {
61062306a36Sopenharmony_ci		ret = PTR_ERR(from_ref);
61162306a36Sopenharmony_ci		goto error2;
61262306a36Sopenharmony_ci	}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	to_ref = lookup_user_key(to_ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
61562306a36Sopenharmony_ci	if (IS_ERR(to_ref)) {
61662306a36Sopenharmony_ci		ret = PTR_ERR(to_ref);
61762306a36Sopenharmony_ci		goto error3;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	ret = key_move(key_ref_to_ptr(key_ref), key_ref_to_ptr(from_ref),
62162306a36Sopenharmony_ci		       key_ref_to_ptr(to_ref), flags);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	key_ref_put(to_ref);
62462306a36Sopenharmony_cierror3:
62562306a36Sopenharmony_ci	key_ref_put(from_ref);
62662306a36Sopenharmony_cierror2:
62762306a36Sopenharmony_ci	key_ref_put(key_ref);
62862306a36Sopenharmony_ci	return ret;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci/*
63262306a36Sopenharmony_ci * Return a description of a key to userspace.
63362306a36Sopenharmony_ci *
63462306a36Sopenharmony_ci * The key must grant the caller View permission for this to work.
63562306a36Sopenharmony_ci *
63662306a36Sopenharmony_ci * If there's a buffer, we place up to buflen bytes of data into it formatted
63762306a36Sopenharmony_ci * in the following way:
63862306a36Sopenharmony_ci *
63962306a36Sopenharmony_ci *	type;uid;gid;perm;description<NUL>
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci * If successful, we return the amount of description available, irrespective
64262306a36Sopenharmony_ci * of how much we may have copied into the buffer.
64362306a36Sopenharmony_ci */
64462306a36Sopenharmony_cilong keyctl_describe_key(key_serial_t keyid,
64562306a36Sopenharmony_ci			 char __user *buffer,
64662306a36Sopenharmony_ci			 size_t buflen)
64762306a36Sopenharmony_ci{
64862306a36Sopenharmony_ci	struct key *key, *instkey;
64962306a36Sopenharmony_ci	key_ref_t key_ref;
65062306a36Sopenharmony_ci	char *infobuf;
65162306a36Sopenharmony_ci	long ret;
65262306a36Sopenharmony_ci	int desclen, infolen;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);
65562306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
65662306a36Sopenharmony_ci		/* viewing a key under construction is permitted if we have the
65762306a36Sopenharmony_ci		 * authorisation token handy */
65862306a36Sopenharmony_ci		if (PTR_ERR(key_ref) == -EACCES) {
65962306a36Sopenharmony_ci			instkey = key_get_instantiation_authkey(keyid);
66062306a36Sopenharmony_ci			if (!IS_ERR(instkey)) {
66162306a36Sopenharmony_ci				key_put(instkey);
66262306a36Sopenharmony_ci				key_ref = lookup_user_key(keyid,
66362306a36Sopenharmony_ci							  KEY_LOOKUP_PARTIAL,
66462306a36Sopenharmony_ci							  KEY_AUTHTOKEN_OVERRIDE);
66562306a36Sopenharmony_ci				if (!IS_ERR(key_ref))
66662306a36Sopenharmony_ci					goto okay;
66762306a36Sopenharmony_ci			}
66862306a36Sopenharmony_ci		}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
67162306a36Sopenharmony_ci		goto error;
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ciokay:
67562306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
67662306a36Sopenharmony_ci	desclen = strlen(key->description);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	/* calculate how much information we're going to return */
67962306a36Sopenharmony_ci	ret = -ENOMEM;
68062306a36Sopenharmony_ci	infobuf = kasprintf(GFP_KERNEL,
68162306a36Sopenharmony_ci			    "%s;%d;%d;%08x;",
68262306a36Sopenharmony_ci			    key->type->name,
68362306a36Sopenharmony_ci			    from_kuid_munged(current_user_ns(), key->uid),
68462306a36Sopenharmony_ci			    from_kgid_munged(current_user_ns(), key->gid),
68562306a36Sopenharmony_ci			    key->perm);
68662306a36Sopenharmony_ci	if (!infobuf)
68762306a36Sopenharmony_ci		goto error2;
68862306a36Sopenharmony_ci	infolen = strlen(infobuf);
68962306a36Sopenharmony_ci	ret = infolen + desclen + 1;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	/* consider returning the data */
69262306a36Sopenharmony_ci	if (buffer && buflen >= ret) {
69362306a36Sopenharmony_ci		if (copy_to_user(buffer, infobuf, infolen) != 0 ||
69462306a36Sopenharmony_ci		    copy_to_user(buffer + infolen, key->description,
69562306a36Sopenharmony_ci				 desclen + 1) != 0)
69662306a36Sopenharmony_ci			ret = -EFAULT;
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	kfree(infobuf);
70062306a36Sopenharmony_cierror2:
70162306a36Sopenharmony_ci	key_ref_put(key_ref);
70262306a36Sopenharmony_cierror:
70362306a36Sopenharmony_ci	return ret;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci/*
70762306a36Sopenharmony_ci * Search the specified keyring and any keyrings it links to for a matching
70862306a36Sopenharmony_ci * key.  Only keyrings that grant the caller Search permission will be searched
70962306a36Sopenharmony_ci * (this includes the starting keyring).  Only keys with Search permission can
71062306a36Sopenharmony_ci * be found.
71162306a36Sopenharmony_ci *
71262306a36Sopenharmony_ci * If successful, the found key will be linked to the destination keyring if
71362306a36Sopenharmony_ci * supplied and the key has Link permission, and the found key ID will be
71462306a36Sopenharmony_ci * returned.
71562306a36Sopenharmony_ci */
71662306a36Sopenharmony_cilong keyctl_keyring_search(key_serial_t ringid,
71762306a36Sopenharmony_ci			   const char __user *_type,
71862306a36Sopenharmony_ci			   const char __user *_description,
71962306a36Sopenharmony_ci			   key_serial_t destringid)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct key_type *ktype;
72262306a36Sopenharmony_ci	key_ref_t keyring_ref, key_ref, dest_ref;
72362306a36Sopenharmony_ci	char type[32], *description;
72462306a36Sopenharmony_ci	long ret;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	/* pull the type and description into kernel space */
72762306a36Sopenharmony_ci	ret = key_get_type_from_user(type, _type, sizeof(type));
72862306a36Sopenharmony_ci	if (ret < 0)
72962306a36Sopenharmony_ci		goto error;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	description = strndup_user(_description, KEY_MAX_DESC_SIZE);
73262306a36Sopenharmony_ci	if (IS_ERR(description)) {
73362306a36Sopenharmony_ci		ret = PTR_ERR(description);
73462306a36Sopenharmony_ci		goto error;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* get the keyring at which to begin the search */
73862306a36Sopenharmony_ci	keyring_ref = lookup_user_key(ringid, 0, KEY_NEED_SEARCH);
73962306a36Sopenharmony_ci	if (IS_ERR(keyring_ref)) {
74062306a36Sopenharmony_ci		ret = PTR_ERR(keyring_ref);
74162306a36Sopenharmony_ci		goto error2;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* get the destination keyring if specified */
74562306a36Sopenharmony_ci	dest_ref = NULL;
74662306a36Sopenharmony_ci	if (destringid) {
74762306a36Sopenharmony_ci		dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
74862306a36Sopenharmony_ci					   KEY_NEED_WRITE);
74962306a36Sopenharmony_ci		if (IS_ERR(dest_ref)) {
75062306a36Sopenharmony_ci			ret = PTR_ERR(dest_ref);
75162306a36Sopenharmony_ci			goto error3;
75262306a36Sopenharmony_ci		}
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* find the key type */
75662306a36Sopenharmony_ci	ktype = key_type_lookup(type);
75762306a36Sopenharmony_ci	if (IS_ERR(ktype)) {
75862306a36Sopenharmony_ci		ret = PTR_ERR(ktype);
75962306a36Sopenharmony_ci		goto error4;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* do the search */
76362306a36Sopenharmony_ci	key_ref = keyring_search(keyring_ref, ktype, description, true);
76462306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
76562306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		/* treat lack or presence of a negative key the same */
76862306a36Sopenharmony_ci		if (ret == -EAGAIN)
76962306a36Sopenharmony_ci			ret = -ENOKEY;
77062306a36Sopenharmony_ci		goto error5;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	/* link the resulting key to the destination keyring if we can */
77462306a36Sopenharmony_ci	if (dest_ref) {
77562306a36Sopenharmony_ci		ret = key_permission(key_ref, KEY_NEED_LINK);
77662306a36Sopenharmony_ci		if (ret < 0)
77762306a36Sopenharmony_ci			goto error6;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci		ret = key_link(key_ref_to_ptr(dest_ref), key_ref_to_ptr(key_ref));
78062306a36Sopenharmony_ci		if (ret < 0)
78162306a36Sopenharmony_ci			goto error6;
78262306a36Sopenharmony_ci	}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	ret = key_ref_to_ptr(key_ref)->serial;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cierror6:
78762306a36Sopenharmony_ci	key_ref_put(key_ref);
78862306a36Sopenharmony_cierror5:
78962306a36Sopenharmony_ci	key_type_put(ktype);
79062306a36Sopenharmony_cierror4:
79162306a36Sopenharmony_ci	key_ref_put(dest_ref);
79262306a36Sopenharmony_cierror3:
79362306a36Sopenharmony_ci	key_ref_put(keyring_ref);
79462306a36Sopenharmony_cierror2:
79562306a36Sopenharmony_ci	kfree(description);
79662306a36Sopenharmony_cierror:
79762306a36Sopenharmony_ci	return ret;
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci/*
80162306a36Sopenharmony_ci * Call the read method
80262306a36Sopenharmony_ci */
80362306a36Sopenharmony_cistatic long __keyctl_read_key(struct key *key, char *buffer, size_t buflen)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	long ret;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	down_read(&key->sem);
80862306a36Sopenharmony_ci	ret = key_validate(key);
80962306a36Sopenharmony_ci	if (ret == 0)
81062306a36Sopenharmony_ci		ret = key->type->read(key, buffer, buflen);
81162306a36Sopenharmony_ci	up_read(&key->sem);
81262306a36Sopenharmony_ci	return ret;
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci/*
81662306a36Sopenharmony_ci * Read a key's payload.
81762306a36Sopenharmony_ci *
81862306a36Sopenharmony_ci * The key must either grant the caller Read permission, or it must grant the
81962306a36Sopenharmony_ci * caller Search permission when searched for from the process keyrings.
82062306a36Sopenharmony_ci *
82162306a36Sopenharmony_ci * If successful, we place up to buflen bytes of data into the buffer, if one
82262306a36Sopenharmony_ci * is provided, and return the amount of data that is available in the key,
82362306a36Sopenharmony_ci * irrespective of how much we copied into the buffer.
82462306a36Sopenharmony_ci */
82562306a36Sopenharmony_cilong keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct key *key;
82862306a36Sopenharmony_ci	key_ref_t key_ref;
82962306a36Sopenharmony_ci	long ret;
83062306a36Sopenharmony_ci	char *key_data = NULL;
83162306a36Sopenharmony_ci	size_t key_data_len;
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	/* find the key first */
83462306a36Sopenharmony_ci	key_ref = lookup_user_key(keyid, 0, KEY_DEFER_PERM_CHECK);
83562306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
83662306a36Sopenharmony_ci		ret = -ENOKEY;
83762306a36Sopenharmony_ci		goto out;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	ret = key_read_state(key);
84362306a36Sopenharmony_ci	if (ret < 0)
84462306a36Sopenharmony_ci		goto key_put_out; /* Negatively instantiated */
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	/* see if we can read it directly */
84762306a36Sopenharmony_ci	ret = key_permission(key_ref, KEY_NEED_READ);
84862306a36Sopenharmony_ci	if (ret == 0)
84962306a36Sopenharmony_ci		goto can_read_key;
85062306a36Sopenharmony_ci	if (ret != -EACCES)
85162306a36Sopenharmony_ci		goto key_put_out;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* we can't; see if it's searchable from this process's keyrings
85462306a36Sopenharmony_ci	 * - we automatically take account of the fact that it may be
85562306a36Sopenharmony_ci	 *   dangling off an instantiation key
85662306a36Sopenharmony_ci	 */
85762306a36Sopenharmony_ci	if (!is_key_possessed(key_ref)) {
85862306a36Sopenharmony_ci		ret = -EACCES;
85962306a36Sopenharmony_ci		goto key_put_out;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* the key is probably readable - now try to read it */
86362306a36Sopenharmony_cican_read_key:
86462306a36Sopenharmony_ci	if (!key->type->read) {
86562306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
86662306a36Sopenharmony_ci		goto key_put_out;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (!buffer || !buflen) {
87062306a36Sopenharmony_ci		/* Get the key length from the read method */
87162306a36Sopenharmony_ci		ret = __keyctl_read_key(key, NULL, 0);
87262306a36Sopenharmony_ci		goto key_put_out;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	/*
87662306a36Sopenharmony_ci	 * Read the data with the semaphore held (since we might sleep)
87762306a36Sopenharmony_ci	 * to protect against the key being updated or revoked.
87862306a36Sopenharmony_ci	 *
87962306a36Sopenharmony_ci	 * Allocating a temporary buffer to hold the keys before
88062306a36Sopenharmony_ci	 * transferring them to user buffer to avoid potential
88162306a36Sopenharmony_ci	 * deadlock involving page fault and mmap_lock.
88262306a36Sopenharmony_ci	 *
88362306a36Sopenharmony_ci	 * key_data_len = (buflen <= PAGE_SIZE)
88462306a36Sopenharmony_ci	 *		? buflen : actual length of key data
88562306a36Sopenharmony_ci	 *
88662306a36Sopenharmony_ci	 * This prevents allocating arbitrary large buffer which can
88762306a36Sopenharmony_ci	 * be much larger than the actual key length. In the latter case,
88862306a36Sopenharmony_ci	 * at least 2 passes of this loop is required.
88962306a36Sopenharmony_ci	 */
89062306a36Sopenharmony_ci	key_data_len = (buflen <= PAGE_SIZE) ? buflen : 0;
89162306a36Sopenharmony_ci	for (;;) {
89262306a36Sopenharmony_ci		if (key_data_len) {
89362306a36Sopenharmony_ci			key_data = kvmalloc(key_data_len, GFP_KERNEL);
89462306a36Sopenharmony_ci			if (!key_data) {
89562306a36Sopenharmony_ci				ret = -ENOMEM;
89662306a36Sopenharmony_ci				goto key_put_out;
89762306a36Sopenharmony_ci			}
89862306a36Sopenharmony_ci		}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci		ret = __keyctl_read_key(key, key_data, key_data_len);
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci		/*
90362306a36Sopenharmony_ci		 * Read methods will just return the required length without
90462306a36Sopenharmony_ci		 * any copying if the provided length isn't large enough.
90562306a36Sopenharmony_ci		 */
90662306a36Sopenharmony_ci		if (ret <= 0 || ret > buflen)
90762306a36Sopenharmony_ci			break;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		/*
91062306a36Sopenharmony_ci		 * The key may change (unlikely) in between 2 consecutive
91162306a36Sopenharmony_ci		 * __keyctl_read_key() calls. In this case, we reallocate
91262306a36Sopenharmony_ci		 * a larger buffer and redo the key read when
91362306a36Sopenharmony_ci		 * key_data_len < ret <= buflen.
91462306a36Sopenharmony_ci		 */
91562306a36Sopenharmony_ci		if (ret > key_data_len) {
91662306a36Sopenharmony_ci			if (unlikely(key_data))
91762306a36Sopenharmony_ci				kvfree_sensitive(key_data, key_data_len);
91862306a36Sopenharmony_ci			key_data_len = ret;
91962306a36Sopenharmony_ci			continue;	/* Allocate buffer */
92062306a36Sopenharmony_ci		}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (copy_to_user(buffer, key_data, ret))
92362306a36Sopenharmony_ci			ret = -EFAULT;
92462306a36Sopenharmony_ci		break;
92562306a36Sopenharmony_ci	}
92662306a36Sopenharmony_ci	kvfree_sensitive(key_data, key_data_len);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_cikey_put_out:
92962306a36Sopenharmony_ci	key_put(key);
93062306a36Sopenharmony_ciout:
93162306a36Sopenharmony_ci	return ret;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci/*
93562306a36Sopenharmony_ci * Change the ownership of a key
93662306a36Sopenharmony_ci *
93762306a36Sopenharmony_ci * The key must grant the caller Setattr permission for this to work, though
93862306a36Sopenharmony_ci * the key need not be fully instantiated yet.  For the UID to be changed, or
93962306a36Sopenharmony_ci * for the GID to be changed to a group the caller is not a member of, the
94062306a36Sopenharmony_ci * caller must have sysadmin capability.  If either uid or gid is -1 then that
94162306a36Sopenharmony_ci * attribute is not changed.
94262306a36Sopenharmony_ci *
94362306a36Sopenharmony_ci * If the UID is to be changed, the new user must have sufficient quota to
94462306a36Sopenharmony_ci * accept the key.  The quota deduction will be removed from the old user to
94562306a36Sopenharmony_ci * the new user should the attribute be changed.
94662306a36Sopenharmony_ci *
94762306a36Sopenharmony_ci * If successful, 0 will be returned.
94862306a36Sopenharmony_ci */
94962306a36Sopenharmony_cilong keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct key_user *newowner, *zapowner = NULL;
95262306a36Sopenharmony_ci	struct key *key;
95362306a36Sopenharmony_ci	key_ref_t key_ref;
95462306a36Sopenharmony_ci	long ret;
95562306a36Sopenharmony_ci	kuid_t uid;
95662306a36Sopenharmony_ci	kgid_t gid;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	uid = make_kuid(current_user_ns(), user);
95962306a36Sopenharmony_ci	gid = make_kgid(current_user_ns(), group);
96062306a36Sopenharmony_ci	ret = -EINVAL;
96162306a36Sopenharmony_ci	if ((user != (uid_t) -1) && !uid_valid(uid))
96262306a36Sopenharmony_ci		goto error;
96362306a36Sopenharmony_ci	if ((group != (gid_t) -1) && !gid_valid(gid))
96462306a36Sopenharmony_ci		goto error;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	ret = 0;
96762306a36Sopenharmony_ci	if (user == (uid_t) -1 && group == (gid_t) -1)
96862306a36Sopenharmony_ci		goto error;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
97162306a36Sopenharmony_ci				  KEY_NEED_SETATTR);
97262306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
97362306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
97462306a36Sopenharmony_ci		goto error;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* make the changes with the locks held to prevent chown/chown races */
98062306a36Sopenharmony_ci	ret = -EACCES;
98162306a36Sopenharmony_ci	down_write(&key->sem);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	{
98462306a36Sopenharmony_ci		bool is_privileged_op = false;
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_ci		/* only the sysadmin can chown a key to some other UID */
98762306a36Sopenharmony_ci		if (user != (uid_t) -1 && !uid_eq(key->uid, uid))
98862306a36Sopenharmony_ci			is_privileged_op = true;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		/* only the sysadmin can set the key's GID to a group other
99162306a36Sopenharmony_ci		 * than one of those that the current process subscribes to */
99262306a36Sopenharmony_ci		if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid))
99362306a36Sopenharmony_ci			is_privileged_op = true;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci		if (is_privileged_op && !capable(CAP_SYS_ADMIN))
99662306a36Sopenharmony_ci			goto error_put;
99762306a36Sopenharmony_ci	}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/* change the UID */
100062306a36Sopenharmony_ci	if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) {
100162306a36Sopenharmony_ci		ret = -ENOMEM;
100262306a36Sopenharmony_ci		newowner = key_user_lookup(uid);
100362306a36Sopenharmony_ci		if (!newowner)
100462306a36Sopenharmony_ci			goto error_put;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci		/* transfer the quota burden to the new user */
100762306a36Sopenharmony_ci		if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
100862306a36Sopenharmony_ci			unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
100962306a36Sopenharmony_ci				key_quota_root_maxkeys : key_quota_maxkeys;
101062306a36Sopenharmony_ci			unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
101162306a36Sopenharmony_ci				key_quota_root_maxbytes : key_quota_maxbytes;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci			spin_lock(&newowner->lock);
101462306a36Sopenharmony_ci			if (newowner->qnkeys + 1 > maxkeys ||
101562306a36Sopenharmony_ci			    newowner->qnbytes + key->quotalen > maxbytes ||
101662306a36Sopenharmony_ci			    newowner->qnbytes + key->quotalen <
101762306a36Sopenharmony_ci			    newowner->qnbytes)
101862306a36Sopenharmony_ci				goto quota_overrun;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci			newowner->qnkeys++;
102162306a36Sopenharmony_ci			newowner->qnbytes += key->quotalen;
102262306a36Sopenharmony_ci			spin_unlock(&newowner->lock);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci			spin_lock(&key->user->lock);
102562306a36Sopenharmony_ci			key->user->qnkeys--;
102662306a36Sopenharmony_ci			key->user->qnbytes -= key->quotalen;
102762306a36Sopenharmony_ci			spin_unlock(&key->user->lock);
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci		atomic_dec(&key->user->nkeys);
103162306a36Sopenharmony_ci		atomic_inc(&newowner->nkeys);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci		if (key->state != KEY_IS_UNINSTANTIATED) {
103462306a36Sopenharmony_ci			atomic_dec(&key->user->nikeys);
103562306a36Sopenharmony_ci			atomic_inc(&newowner->nikeys);
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci		zapowner = key->user;
103962306a36Sopenharmony_ci		key->user = newowner;
104062306a36Sopenharmony_ci		key->uid = uid;
104162306a36Sopenharmony_ci	}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/* change the GID */
104462306a36Sopenharmony_ci	if (group != (gid_t) -1)
104562306a36Sopenharmony_ci		key->gid = gid;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	notify_key(key, NOTIFY_KEY_SETATTR, 0);
104862306a36Sopenharmony_ci	ret = 0;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cierror_put:
105162306a36Sopenharmony_ci	up_write(&key->sem);
105262306a36Sopenharmony_ci	key_put(key);
105362306a36Sopenharmony_ci	if (zapowner)
105462306a36Sopenharmony_ci		key_user_put(zapowner);
105562306a36Sopenharmony_cierror:
105662306a36Sopenharmony_ci	return ret;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ciquota_overrun:
105962306a36Sopenharmony_ci	spin_unlock(&newowner->lock);
106062306a36Sopenharmony_ci	zapowner = newowner;
106162306a36Sopenharmony_ci	ret = -EDQUOT;
106262306a36Sopenharmony_ci	goto error_put;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_ci/*
106662306a36Sopenharmony_ci * Change the permission mask on a key.
106762306a36Sopenharmony_ci *
106862306a36Sopenharmony_ci * The key must grant the caller Setattr permission for this to work, though
106962306a36Sopenharmony_ci * the key need not be fully instantiated yet.  If the caller does not have
107062306a36Sopenharmony_ci * sysadmin capability, it may only change the permission on keys that it owns.
107162306a36Sopenharmony_ci */
107262306a36Sopenharmony_cilong keyctl_setperm_key(key_serial_t id, key_perm_t perm)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct key *key;
107562306a36Sopenharmony_ci	key_ref_t key_ref;
107662306a36Sopenharmony_ci	long ret;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	ret = -EINVAL;
107962306a36Sopenharmony_ci	if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
108062306a36Sopenharmony_ci		goto error;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
108362306a36Sopenharmony_ci				  KEY_NEED_SETATTR);
108462306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
108562306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
108662306a36Sopenharmony_ci		goto error;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* make the changes with the locks held to prevent chown/chmod races */
109262306a36Sopenharmony_ci	ret = -EACCES;
109362306a36Sopenharmony_ci	down_write(&key->sem);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	/* if we're not the sysadmin, we can only change a key that we own */
109662306a36Sopenharmony_ci	if (uid_eq(key->uid, current_fsuid()) || capable(CAP_SYS_ADMIN)) {
109762306a36Sopenharmony_ci		key->perm = perm;
109862306a36Sopenharmony_ci		notify_key(key, NOTIFY_KEY_SETATTR, 0);
109962306a36Sopenharmony_ci		ret = 0;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	up_write(&key->sem);
110362306a36Sopenharmony_ci	key_put(key);
110462306a36Sopenharmony_cierror:
110562306a36Sopenharmony_ci	return ret;
110662306a36Sopenharmony_ci}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci/*
110962306a36Sopenharmony_ci * Get the destination keyring for instantiation and check that the caller has
111062306a36Sopenharmony_ci * Write permission on it.
111162306a36Sopenharmony_ci */
111262306a36Sopenharmony_cistatic long get_instantiation_keyring(key_serial_t ringid,
111362306a36Sopenharmony_ci				      struct request_key_auth *rka,
111462306a36Sopenharmony_ci				      struct key **_dest_keyring)
111562306a36Sopenharmony_ci{
111662306a36Sopenharmony_ci	key_ref_t dkref;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	*_dest_keyring = NULL;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* just return a NULL pointer if we weren't asked to make a link */
112162306a36Sopenharmony_ci	if (ringid == 0)
112262306a36Sopenharmony_ci		return 0;
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	/* if a specific keyring is nominated by ID, then use that */
112562306a36Sopenharmony_ci	if (ringid > 0) {
112662306a36Sopenharmony_ci		dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_NEED_WRITE);
112762306a36Sopenharmony_ci		if (IS_ERR(dkref))
112862306a36Sopenharmony_ci			return PTR_ERR(dkref);
112962306a36Sopenharmony_ci		*_dest_keyring = key_ref_to_ptr(dkref);
113062306a36Sopenharmony_ci		return 0;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci	if (ringid == KEY_SPEC_REQKEY_AUTH_KEY)
113462306a36Sopenharmony_ci		return -EINVAL;
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	/* otherwise specify the destination keyring recorded in the
113762306a36Sopenharmony_ci	 * authorisation key (any KEY_SPEC_*_KEYRING) */
113862306a36Sopenharmony_ci	if (ringid >= KEY_SPEC_REQUESTOR_KEYRING) {
113962306a36Sopenharmony_ci		*_dest_keyring = key_get(rka->dest_keyring);
114062306a36Sopenharmony_ci		return 0;
114162306a36Sopenharmony_ci	}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	return -ENOKEY;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/*
114762306a36Sopenharmony_ci * Change the request_key authorisation key on the current process.
114862306a36Sopenharmony_ci */
114962306a36Sopenharmony_cistatic int keyctl_change_reqkey_auth(struct key *key)
115062306a36Sopenharmony_ci{
115162306a36Sopenharmony_ci	struct cred *new;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	new = prepare_creds();
115462306a36Sopenharmony_ci	if (!new)
115562306a36Sopenharmony_ci		return -ENOMEM;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	key_put(new->request_key_auth);
115862306a36Sopenharmony_ci	new->request_key_auth = key_get(key);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	return commit_creds(new);
116162306a36Sopenharmony_ci}
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci/*
116462306a36Sopenharmony_ci * Instantiate a key with the specified payload and link the key into the
116562306a36Sopenharmony_ci * destination keyring if one is given.
116662306a36Sopenharmony_ci *
116762306a36Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to
116862306a36Sopenharmony_ci * work (see keyctl_assume_authority).  No other permissions are required.
116962306a36Sopenharmony_ci *
117062306a36Sopenharmony_ci * If successful, 0 will be returned.
117162306a36Sopenharmony_ci */
117262306a36Sopenharmony_cistatic long keyctl_instantiate_key_common(key_serial_t id,
117362306a36Sopenharmony_ci				   struct iov_iter *from,
117462306a36Sopenharmony_ci				   key_serial_t ringid)
117562306a36Sopenharmony_ci{
117662306a36Sopenharmony_ci	const struct cred *cred = current_cred();
117762306a36Sopenharmony_ci	struct request_key_auth *rka;
117862306a36Sopenharmony_ci	struct key *instkey, *dest_keyring;
117962306a36Sopenharmony_ci	size_t plen = from ? iov_iter_count(from) : 0;
118062306a36Sopenharmony_ci	void *payload;
118162306a36Sopenharmony_ci	long ret;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci	kenter("%d,,%zu,%d", id, plen, ringid);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	if (!plen)
118662306a36Sopenharmony_ci		from = NULL;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	ret = -EINVAL;
118962306a36Sopenharmony_ci	if (plen > 1024 * 1024 - 1)
119062306a36Sopenharmony_ci		goto error;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	/* the appropriate instantiation authorisation key must have been
119362306a36Sopenharmony_ci	 * assumed before calling this */
119462306a36Sopenharmony_ci	ret = -EPERM;
119562306a36Sopenharmony_ci	instkey = cred->request_key_auth;
119662306a36Sopenharmony_ci	if (!instkey)
119762306a36Sopenharmony_ci		goto error;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci	rka = instkey->payload.data[0];
120062306a36Sopenharmony_ci	if (rka->target_key->serial != id)
120162306a36Sopenharmony_ci		goto error;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	/* pull the payload in if one was supplied */
120462306a36Sopenharmony_ci	payload = NULL;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	if (from) {
120762306a36Sopenharmony_ci		ret = -ENOMEM;
120862306a36Sopenharmony_ci		payload = kvmalloc(plen, GFP_KERNEL);
120962306a36Sopenharmony_ci		if (!payload)
121062306a36Sopenharmony_ci			goto error;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		ret = -EFAULT;
121362306a36Sopenharmony_ci		if (!copy_from_iter_full(payload, plen, from))
121462306a36Sopenharmony_ci			goto error2;
121562306a36Sopenharmony_ci	}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	/* find the destination keyring amongst those belonging to the
121862306a36Sopenharmony_ci	 * requesting task */
121962306a36Sopenharmony_ci	ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
122062306a36Sopenharmony_ci	if (ret < 0)
122162306a36Sopenharmony_ci		goto error2;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	/* instantiate the key and link it into a keyring */
122462306a36Sopenharmony_ci	ret = key_instantiate_and_link(rka->target_key, payload, plen,
122562306a36Sopenharmony_ci				       dest_keyring, instkey);
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	key_put(dest_keyring);
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	/* discard the assumed authority if it's just been disabled by
123062306a36Sopenharmony_ci	 * instantiation of the key */
123162306a36Sopenharmony_ci	if (ret == 0)
123262306a36Sopenharmony_ci		keyctl_change_reqkey_auth(NULL);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cierror2:
123562306a36Sopenharmony_ci	kvfree_sensitive(payload, plen);
123662306a36Sopenharmony_cierror:
123762306a36Sopenharmony_ci	return ret;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci/*
124162306a36Sopenharmony_ci * Instantiate a key with the specified payload and link the key into the
124262306a36Sopenharmony_ci * destination keyring if one is given.
124362306a36Sopenharmony_ci *
124462306a36Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to
124562306a36Sopenharmony_ci * work (see keyctl_assume_authority).  No other permissions are required.
124662306a36Sopenharmony_ci *
124762306a36Sopenharmony_ci * If successful, 0 will be returned.
124862306a36Sopenharmony_ci */
124962306a36Sopenharmony_cilong keyctl_instantiate_key(key_serial_t id,
125062306a36Sopenharmony_ci			    const void __user *_payload,
125162306a36Sopenharmony_ci			    size_t plen,
125262306a36Sopenharmony_ci			    key_serial_t ringid)
125362306a36Sopenharmony_ci{
125462306a36Sopenharmony_ci	if (_payload && plen) {
125562306a36Sopenharmony_ci		struct iovec iov;
125662306a36Sopenharmony_ci		struct iov_iter from;
125762306a36Sopenharmony_ci		int ret;
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci		ret = import_single_range(ITER_SOURCE, (void __user *)_payload, plen,
126062306a36Sopenharmony_ci					  &iov, &from);
126162306a36Sopenharmony_ci		if (unlikely(ret))
126262306a36Sopenharmony_ci			return ret;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci		return keyctl_instantiate_key_common(id, &from, ringid);
126562306a36Sopenharmony_ci	}
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	return keyctl_instantiate_key_common(id, NULL, ringid);
126862306a36Sopenharmony_ci}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci/*
127162306a36Sopenharmony_ci * Instantiate a key with the specified multipart payload and link the key into
127262306a36Sopenharmony_ci * the destination keyring if one is given.
127362306a36Sopenharmony_ci *
127462306a36Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to
127562306a36Sopenharmony_ci * work (see keyctl_assume_authority).  No other permissions are required.
127662306a36Sopenharmony_ci *
127762306a36Sopenharmony_ci * If successful, 0 will be returned.
127862306a36Sopenharmony_ci */
127962306a36Sopenharmony_cilong keyctl_instantiate_key_iov(key_serial_t id,
128062306a36Sopenharmony_ci				const struct iovec __user *_payload_iov,
128162306a36Sopenharmony_ci				unsigned ioc,
128262306a36Sopenharmony_ci				key_serial_t ringid)
128362306a36Sopenharmony_ci{
128462306a36Sopenharmony_ci	struct iovec iovstack[UIO_FASTIOV], *iov = iovstack;
128562306a36Sopenharmony_ci	struct iov_iter from;
128662306a36Sopenharmony_ci	long ret;
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	if (!_payload_iov)
128962306a36Sopenharmony_ci		ioc = 0;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	ret = import_iovec(ITER_SOURCE, _payload_iov, ioc,
129262306a36Sopenharmony_ci				    ARRAY_SIZE(iovstack), &iov, &from);
129362306a36Sopenharmony_ci	if (ret < 0)
129462306a36Sopenharmony_ci		return ret;
129562306a36Sopenharmony_ci	ret = keyctl_instantiate_key_common(id, &from, ringid);
129662306a36Sopenharmony_ci	kfree(iov);
129762306a36Sopenharmony_ci	return ret;
129862306a36Sopenharmony_ci}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci/*
130162306a36Sopenharmony_ci * Negatively instantiate the key with the given timeout (in seconds) and link
130262306a36Sopenharmony_ci * the key into the destination keyring if one is given.
130362306a36Sopenharmony_ci *
130462306a36Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to
130562306a36Sopenharmony_ci * work (see keyctl_assume_authority).  No other permissions are required.
130662306a36Sopenharmony_ci *
130762306a36Sopenharmony_ci * The key and any links to the key will be automatically garbage collected
130862306a36Sopenharmony_ci * after the timeout expires.
130962306a36Sopenharmony_ci *
131062306a36Sopenharmony_ci * Negative keys are used to rate limit repeated request_key() calls by causing
131162306a36Sopenharmony_ci * them to return -ENOKEY until the negative key expires.
131262306a36Sopenharmony_ci *
131362306a36Sopenharmony_ci * If successful, 0 will be returned.
131462306a36Sopenharmony_ci */
131562306a36Sopenharmony_cilong keyctl_negate_key(key_serial_t id, unsigned timeout, key_serial_t ringid)
131662306a36Sopenharmony_ci{
131762306a36Sopenharmony_ci	return keyctl_reject_key(id, timeout, ENOKEY, ringid);
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci/*
132162306a36Sopenharmony_ci * Negatively instantiate the key with the given timeout (in seconds) and error
132262306a36Sopenharmony_ci * code and link the key into the destination keyring if one is given.
132362306a36Sopenharmony_ci *
132462306a36Sopenharmony_ci * The caller must have the appropriate instantiation permit set for this to
132562306a36Sopenharmony_ci * work (see keyctl_assume_authority).  No other permissions are required.
132662306a36Sopenharmony_ci *
132762306a36Sopenharmony_ci * The key and any links to the key will be automatically garbage collected
132862306a36Sopenharmony_ci * after the timeout expires.
132962306a36Sopenharmony_ci *
133062306a36Sopenharmony_ci * Negative keys are used to rate limit repeated request_key() calls by causing
133162306a36Sopenharmony_ci * them to return the specified error code until the negative key expires.
133262306a36Sopenharmony_ci *
133362306a36Sopenharmony_ci * If successful, 0 will be returned.
133462306a36Sopenharmony_ci */
133562306a36Sopenharmony_cilong keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
133662306a36Sopenharmony_ci		       key_serial_t ringid)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	const struct cred *cred = current_cred();
133962306a36Sopenharmony_ci	struct request_key_auth *rka;
134062306a36Sopenharmony_ci	struct key *instkey, *dest_keyring;
134162306a36Sopenharmony_ci	long ret;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	kenter("%d,%u,%u,%d", id, timeout, error, ringid);
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	/* must be a valid error code and mustn't be a kernel special */
134662306a36Sopenharmony_ci	if (error <= 0 ||
134762306a36Sopenharmony_ci	    error >= MAX_ERRNO ||
134862306a36Sopenharmony_ci	    error == ERESTARTSYS ||
134962306a36Sopenharmony_ci	    error == ERESTARTNOINTR ||
135062306a36Sopenharmony_ci	    error == ERESTARTNOHAND ||
135162306a36Sopenharmony_ci	    error == ERESTART_RESTARTBLOCK)
135262306a36Sopenharmony_ci		return -EINVAL;
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci	/* the appropriate instantiation authorisation key must have been
135562306a36Sopenharmony_ci	 * assumed before calling this */
135662306a36Sopenharmony_ci	ret = -EPERM;
135762306a36Sopenharmony_ci	instkey = cred->request_key_auth;
135862306a36Sopenharmony_ci	if (!instkey)
135962306a36Sopenharmony_ci		goto error;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	rka = instkey->payload.data[0];
136262306a36Sopenharmony_ci	if (rka->target_key->serial != id)
136362306a36Sopenharmony_ci		goto error;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	/* find the destination keyring if present (which must also be
136662306a36Sopenharmony_ci	 * writable) */
136762306a36Sopenharmony_ci	ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
136862306a36Sopenharmony_ci	if (ret < 0)
136962306a36Sopenharmony_ci		goto error;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* instantiate the key and link it into a keyring */
137262306a36Sopenharmony_ci	ret = key_reject_and_link(rka->target_key, timeout, error,
137362306a36Sopenharmony_ci				  dest_keyring, instkey);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	key_put(dest_keyring);
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	/* discard the assumed authority if it's just been disabled by
137862306a36Sopenharmony_ci	 * instantiation of the key */
137962306a36Sopenharmony_ci	if (ret == 0)
138062306a36Sopenharmony_ci		keyctl_change_reqkey_auth(NULL);
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_cierror:
138362306a36Sopenharmony_ci	return ret;
138462306a36Sopenharmony_ci}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci/*
138762306a36Sopenharmony_ci * Read or set the default keyring in which request_key() will cache keys and
138862306a36Sopenharmony_ci * return the old setting.
138962306a36Sopenharmony_ci *
139062306a36Sopenharmony_ci * If a thread or process keyring is specified then it will be created if it
139162306a36Sopenharmony_ci * doesn't yet exist.  The old setting will be returned if successful.
139262306a36Sopenharmony_ci */
139362306a36Sopenharmony_cilong keyctl_set_reqkey_keyring(int reqkey_defl)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	struct cred *new;
139662306a36Sopenharmony_ci	int ret, old_setting;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	old_setting = current_cred_xxx(jit_keyring);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	if (reqkey_defl == KEY_REQKEY_DEFL_NO_CHANGE)
140162306a36Sopenharmony_ci		return old_setting;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	new = prepare_creds();
140462306a36Sopenharmony_ci	if (!new)
140562306a36Sopenharmony_ci		return -ENOMEM;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	switch (reqkey_defl) {
140862306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_THREAD_KEYRING:
140962306a36Sopenharmony_ci		ret = install_thread_keyring_to_cred(new);
141062306a36Sopenharmony_ci		if (ret < 0)
141162306a36Sopenharmony_ci			goto error;
141262306a36Sopenharmony_ci		goto set;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_PROCESS_KEYRING:
141562306a36Sopenharmony_ci		ret = install_process_keyring_to_cred(new);
141662306a36Sopenharmony_ci		if (ret < 0)
141762306a36Sopenharmony_ci			goto error;
141862306a36Sopenharmony_ci		goto set;
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_DEFAULT:
142162306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_SESSION_KEYRING:
142262306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_USER_KEYRING:
142362306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
142462306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_REQUESTOR_KEYRING:
142562306a36Sopenharmony_ci		goto set;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_NO_CHANGE:
142862306a36Sopenharmony_ci	case KEY_REQKEY_DEFL_GROUP_KEYRING:
142962306a36Sopenharmony_ci	default:
143062306a36Sopenharmony_ci		ret = -EINVAL;
143162306a36Sopenharmony_ci		goto error;
143262306a36Sopenharmony_ci	}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ciset:
143562306a36Sopenharmony_ci	new->jit_keyring = reqkey_defl;
143662306a36Sopenharmony_ci	commit_creds(new);
143762306a36Sopenharmony_ci	return old_setting;
143862306a36Sopenharmony_cierror:
143962306a36Sopenharmony_ci	abort_creds(new);
144062306a36Sopenharmony_ci	return ret;
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci/*
144462306a36Sopenharmony_ci * Set or clear the timeout on a key.
144562306a36Sopenharmony_ci *
144662306a36Sopenharmony_ci * Either the key must grant the caller Setattr permission or else the caller
144762306a36Sopenharmony_ci * must hold an instantiation authorisation token for the key.
144862306a36Sopenharmony_ci *
144962306a36Sopenharmony_ci * The timeout is either 0 to clear the timeout, or a number of seconds from
145062306a36Sopenharmony_ci * the current time.  The key and any links to the key will be automatically
145162306a36Sopenharmony_ci * garbage collected after the timeout expires.
145262306a36Sopenharmony_ci *
145362306a36Sopenharmony_ci * Keys with KEY_FLAG_KEEP set should not be timed out.
145462306a36Sopenharmony_ci *
145562306a36Sopenharmony_ci * If successful, 0 is returned.
145662306a36Sopenharmony_ci */
145762306a36Sopenharmony_cilong keyctl_set_timeout(key_serial_t id, unsigned timeout)
145862306a36Sopenharmony_ci{
145962306a36Sopenharmony_ci	struct key *key, *instkey;
146062306a36Sopenharmony_ci	key_ref_t key_ref;
146162306a36Sopenharmony_ci	long ret;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
146462306a36Sopenharmony_ci				  KEY_NEED_SETATTR);
146562306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
146662306a36Sopenharmony_ci		/* setting the timeout on a key under construction is permitted
146762306a36Sopenharmony_ci		 * if we have the authorisation token handy */
146862306a36Sopenharmony_ci		if (PTR_ERR(key_ref) == -EACCES) {
146962306a36Sopenharmony_ci			instkey = key_get_instantiation_authkey(id);
147062306a36Sopenharmony_ci			if (!IS_ERR(instkey)) {
147162306a36Sopenharmony_ci				key_put(instkey);
147262306a36Sopenharmony_ci				key_ref = lookup_user_key(id,
147362306a36Sopenharmony_ci							  KEY_LOOKUP_PARTIAL,
147462306a36Sopenharmony_ci							  KEY_AUTHTOKEN_OVERRIDE);
147562306a36Sopenharmony_ci				if (!IS_ERR(key_ref))
147662306a36Sopenharmony_ci					goto okay;
147762306a36Sopenharmony_ci			}
147862306a36Sopenharmony_ci		}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci		ret = PTR_ERR(key_ref);
148162306a36Sopenharmony_ci		goto error;
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ciokay:
148562306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
148662306a36Sopenharmony_ci	ret = 0;
148762306a36Sopenharmony_ci	if (test_bit(KEY_FLAG_KEEP, &key->flags)) {
148862306a36Sopenharmony_ci		ret = -EPERM;
148962306a36Sopenharmony_ci	} else {
149062306a36Sopenharmony_ci		key_set_timeout(key, timeout);
149162306a36Sopenharmony_ci		notify_key(key, NOTIFY_KEY_SETATTR, 0);
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci	key_put(key);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_cierror:
149662306a36Sopenharmony_ci	return ret;
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci/*
150062306a36Sopenharmony_ci * Assume (or clear) the authority to instantiate the specified key.
150162306a36Sopenharmony_ci *
150262306a36Sopenharmony_ci * This sets the authoritative token currently in force for key instantiation.
150362306a36Sopenharmony_ci * This must be done for a key to be instantiated.  It has the effect of making
150462306a36Sopenharmony_ci * available all the keys from the caller of the request_key() that created a
150562306a36Sopenharmony_ci * key to request_key() calls made by the caller of this function.
150662306a36Sopenharmony_ci *
150762306a36Sopenharmony_ci * The caller must have the instantiation key in their process keyrings with a
150862306a36Sopenharmony_ci * Search permission grant available to the caller.
150962306a36Sopenharmony_ci *
151062306a36Sopenharmony_ci * If the ID given is 0, then the setting will be cleared and 0 returned.
151162306a36Sopenharmony_ci *
151262306a36Sopenharmony_ci * If the ID given has a matching an authorisation key, then that key will be
151362306a36Sopenharmony_ci * set and its ID will be returned.  The authorisation key can be read to get
151462306a36Sopenharmony_ci * the callout information passed to request_key().
151562306a36Sopenharmony_ci */
151662306a36Sopenharmony_cilong keyctl_assume_authority(key_serial_t id)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	struct key *authkey;
151962306a36Sopenharmony_ci	long ret;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	/* special key IDs aren't permitted */
152262306a36Sopenharmony_ci	ret = -EINVAL;
152362306a36Sopenharmony_ci	if (id < 0)
152462306a36Sopenharmony_ci		goto error;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	/* we divest ourselves of authority if given an ID of 0 */
152762306a36Sopenharmony_ci	if (id == 0) {
152862306a36Sopenharmony_ci		ret = keyctl_change_reqkey_auth(NULL);
152962306a36Sopenharmony_ci		goto error;
153062306a36Sopenharmony_ci	}
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	/* attempt to assume the authority temporarily granted to us whilst we
153362306a36Sopenharmony_ci	 * instantiate the specified key
153462306a36Sopenharmony_ci	 * - the authorisation key must be in the current task's keyrings
153562306a36Sopenharmony_ci	 *   somewhere
153662306a36Sopenharmony_ci	 */
153762306a36Sopenharmony_ci	authkey = key_get_instantiation_authkey(id);
153862306a36Sopenharmony_ci	if (IS_ERR(authkey)) {
153962306a36Sopenharmony_ci		ret = PTR_ERR(authkey);
154062306a36Sopenharmony_ci		goto error;
154162306a36Sopenharmony_ci	}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	ret = keyctl_change_reqkey_auth(authkey);
154462306a36Sopenharmony_ci	if (ret == 0)
154562306a36Sopenharmony_ci		ret = authkey->serial;
154662306a36Sopenharmony_ci	key_put(authkey);
154762306a36Sopenharmony_cierror:
154862306a36Sopenharmony_ci	return ret;
154962306a36Sopenharmony_ci}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci/*
155262306a36Sopenharmony_ci * Get a key's the LSM security label.
155362306a36Sopenharmony_ci *
155462306a36Sopenharmony_ci * The key must grant the caller View permission for this to work.
155562306a36Sopenharmony_ci *
155662306a36Sopenharmony_ci * If there's a buffer, then up to buflen bytes of data will be placed into it.
155762306a36Sopenharmony_ci *
155862306a36Sopenharmony_ci * If successful, the amount of information available will be returned,
155962306a36Sopenharmony_ci * irrespective of how much was copied (including the terminal NUL).
156062306a36Sopenharmony_ci */
156162306a36Sopenharmony_cilong keyctl_get_security(key_serial_t keyid,
156262306a36Sopenharmony_ci			 char __user *buffer,
156362306a36Sopenharmony_ci			 size_t buflen)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	struct key *key, *instkey;
156662306a36Sopenharmony_ci	key_ref_t key_ref;
156762306a36Sopenharmony_ci	char *context;
156862306a36Sopenharmony_ci	long ret;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_NEED_VIEW);
157162306a36Sopenharmony_ci	if (IS_ERR(key_ref)) {
157262306a36Sopenharmony_ci		if (PTR_ERR(key_ref) != -EACCES)
157362306a36Sopenharmony_ci			return PTR_ERR(key_ref);
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci		/* viewing a key under construction is also permitted if we
157662306a36Sopenharmony_ci		 * have the authorisation token handy */
157762306a36Sopenharmony_ci		instkey = key_get_instantiation_authkey(keyid);
157862306a36Sopenharmony_ci		if (IS_ERR(instkey))
157962306a36Sopenharmony_ci			return PTR_ERR(instkey);
158062306a36Sopenharmony_ci		key_put(instkey);
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_ci		key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL,
158362306a36Sopenharmony_ci					  KEY_AUTHTOKEN_OVERRIDE);
158462306a36Sopenharmony_ci		if (IS_ERR(key_ref))
158562306a36Sopenharmony_ci			return PTR_ERR(key_ref);
158662306a36Sopenharmony_ci	}
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
158962306a36Sopenharmony_ci	ret = security_key_getsecurity(key, &context);
159062306a36Sopenharmony_ci	if (ret == 0) {
159162306a36Sopenharmony_ci		/* if no information was returned, give userspace an empty
159262306a36Sopenharmony_ci		 * string */
159362306a36Sopenharmony_ci		ret = 1;
159462306a36Sopenharmony_ci		if (buffer && buflen > 0 &&
159562306a36Sopenharmony_ci		    copy_to_user(buffer, "", 1) != 0)
159662306a36Sopenharmony_ci			ret = -EFAULT;
159762306a36Sopenharmony_ci	} else if (ret > 0) {
159862306a36Sopenharmony_ci		/* return as much data as there's room for */
159962306a36Sopenharmony_ci		if (buffer && buflen > 0) {
160062306a36Sopenharmony_ci			if (buflen > ret)
160162306a36Sopenharmony_ci				buflen = ret;
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci			if (copy_to_user(buffer, context, buflen) != 0)
160462306a36Sopenharmony_ci				ret = -EFAULT;
160562306a36Sopenharmony_ci		}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		kfree(context);
160862306a36Sopenharmony_ci	}
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	key_ref_put(key_ref);
161162306a36Sopenharmony_ci	return ret;
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci/*
161562306a36Sopenharmony_ci * Attempt to install the calling process's session keyring on the process's
161662306a36Sopenharmony_ci * parent process.
161762306a36Sopenharmony_ci *
161862306a36Sopenharmony_ci * The keyring must exist and must grant the caller LINK permission, and the
161962306a36Sopenharmony_ci * parent process must be single-threaded and must have the same effective
162062306a36Sopenharmony_ci * ownership as this process and mustn't be SUID/SGID.
162162306a36Sopenharmony_ci *
162262306a36Sopenharmony_ci * The keyring will be emplaced on the parent when it next resumes userspace.
162362306a36Sopenharmony_ci *
162462306a36Sopenharmony_ci * If successful, 0 will be returned.
162562306a36Sopenharmony_ci */
162662306a36Sopenharmony_cilong keyctl_session_to_parent(void)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	struct task_struct *me, *parent;
162962306a36Sopenharmony_ci	const struct cred *mycred, *pcred;
163062306a36Sopenharmony_ci	struct callback_head *newwork, *oldwork;
163162306a36Sopenharmony_ci	key_ref_t keyring_r;
163262306a36Sopenharmony_ci	struct cred *cred;
163362306a36Sopenharmony_ci	int ret;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_NEED_LINK);
163662306a36Sopenharmony_ci	if (IS_ERR(keyring_r))
163762306a36Sopenharmony_ci		return PTR_ERR(keyring_r);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	ret = -ENOMEM;
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	/* our parent is going to need a new cred struct, a new tgcred struct
164262306a36Sopenharmony_ci	 * and new security data, so we allocate them here to prevent ENOMEM in
164362306a36Sopenharmony_ci	 * our parent */
164462306a36Sopenharmony_ci	cred = cred_alloc_blank();
164562306a36Sopenharmony_ci	if (!cred)
164662306a36Sopenharmony_ci		goto error_keyring;
164762306a36Sopenharmony_ci	newwork = &cred->rcu;
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	cred->session_keyring = key_ref_to_ptr(keyring_r);
165062306a36Sopenharmony_ci	keyring_r = NULL;
165162306a36Sopenharmony_ci	init_task_work(newwork, key_change_session_keyring);
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci	me = current;
165462306a36Sopenharmony_ci	rcu_read_lock();
165562306a36Sopenharmony_ci	write_lock_irq(&tasklist_lock);
165662306a36Sopenharmony_ci
165762306a36Sopenharmony_ci	ret = -EPERM;
165862306a36Sopenharmony_ci	oldwork = NULL;
165962306a36Sopenharmony_ci	parent = rcu_dereference_protected(me->real_parent,
166062306a36Sopenharmony_ci					   lockdep_is_held(&tasklist_lock));
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	/* the parent mustn't be init and mustn't be a kernel thread */
166362306a36Sopenharmony_ci	if (parent->pid <= 1 || !parent->mm)
166462306a36Sopenharmony_ci		goto unlock;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	/* the parent must be single threaded */
166762306a36Sopenharmony_ci	if (!thread_group_empty(parent))
166862306a36Sopenharmony_ci		goto unlock;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	/* the parent and the child must have different session keyrings or
167162306a36Sopenharmony_ci	 * there's no point */
167262306a36Sopenharmony_ci	mycred = current_cred();
167362306a36Sopenharmony_ci	pcred = __task_cred(parent);
167462306a36Sopenharmony_ci	if (mycred == pcred ||
167562306a36Sopenharmony_ci	    mycred->session_keyring == pcred->session_keyring) {
167662306a36Sopenharmony_ci		ret = 0;
167762306a36Sopenharmony_ci		goto unlock;
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci	/* the parent must have the same effective ownership and mustn't be
168162306a36Sopenharmony_ci	 * SUID/SGID */
168262306a36Sopenharmony_ci	if (!uid_eq(pcred->uid,	 mycred->euid) ||
168362306a36Sopenharmony_ci	    !uid_eq(pcred->euid, mycred->euid) ||
168462306a36Sopenharmony_ci	    !uid_eq(pcred->suid, mycred->euid) ||
168562306a36Sopenharmony_ci	    !gid_eq(pcred->gid,	 mycred->egid) ||
168662306a36Sopenharmony_ci	    !gid_eq(pcred->egid, mycred->egid) ||
168762306a36Sopenharmony_ci	    !gid_eq(pcred->sgid, mycred->egid))
168862306a36Sopenharmony_ci		goto unlock;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	/* the keyrings must have the same UID */
169162306a36Sopenharmony_ci	if ((pcred->session_keyring &&
169262306a36Sopenharmony_ci	     !uid_eq(pcred->session_keyring->uid, mycred->euid)) ||
169362306a36Sopenharmony_ci	    !uid_eq(mycred->session_keyring->uid, mycred->euid))
169462306a36Sopenharmony_ci		goto unlock;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	/* cancel an already pending keyring replacement */
169762306a36Sopenharmony_ci	oldwork = task_work_cancel(parent, key_change_session_keyring);
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	/* the replacement session keyring is applied just prior to userspace
170062306a36Sopenharmony_ci	 * restarting */
170162306a36Sopenharmony_ci	ret = task_work_add(parent, newwork, TWA_RESUME);
170262306a36Sopenharmony_ci	if (!ret)
170362306a36Sopenharmony_ci		newwork = NULL;
170462306a36Sopenharmony_ciunlock:
170562306a36Sopenharmony_ci	write_unlock_irq(&tasklist_lock);
170662306a36Sopenharmony_ci	rcu_read_unlock();
170762306a36Sopenharmony_ci	if (oldwork)
170862306a36Sopenharmony_ci		put_cred(container_of(oldwork, struct cred, rcu));
170962306a36Sopenharmony_ci	if (newwork)
171062306a36Sopenharmony_ci		put_cred(cred);
171162306a36Sopenharmony_ci	return ret;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_cierror_keyring:
171462306a36Sopenharmony_ci	key_ref_put(keyring_r);
171562306a36Sopenharmony_ci	return ret;
171662306a36Sopenharmony_ci}
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci/*
171962306a36Sopenharmony_ci * Apply a restriction to a given keyring.
172062306a36Sopenharmony_ci *
172162306a36Sopenharmony_ci * The caller must have Setattr permission to change keyring restrictions.
172262306a36Sopenharmony_ci *
172362306a36Sopenharmony_ci * The requested type name may be a NULL pointer to reject all attempts
172462306a36Sopenharmony_ci * to link to the keyring.  In this case, _restriction must also be NULL.
172562306a36Sopenharmony_ci * Otherwise, both _type and _restriction must be non-NULL.
172662306a36Sopenharmony_ci *
172762306a36Sopenharmony_ci * Returns 0 if successful.
172862306a36Sopenharmony_ci */
172962306a36Sopenharmony_cilong keyctl_restrict_keyring(key_serial_t id, const char __user *_type,
173062306a36Sopenharmony_ci			     const char __user *_restriction)
173162306a36Sopenharmony_ci{
173262306a36Sopenharmony_ci	key_ref_t key_ref;
173362306a36Sopenharmony_ci	char type[32];
173462306a36Sopenharmony_ci	char *restriction = NULL;
173562306a36Sopenharmony_ci	long ret;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR);
173862306a36Sopenharmony_ci	if (IS_ERR(key_ref))
173962306a36Sopenharmony_ci		return PTR_ERR(key_ref);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	ret = -EINVAL;
174262306a36Sopenharmony_ci	if (_type) {
174362306a36Sopenharmony_ci		if (!_restriction)
174462306a36Sopenharmony_ci			goto error;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci		ret = key_get_type_from_user(type, _type, sizeof(type));
174762306a36Sopenharmony_ci		if (ret < 0)
174862306a36Sopenharmony_ci			goto error;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci		restriction = strndup_user(_restriction, PAGE_SIZE);
175162306a36Sopenharmony_ci		if (IS_ERR(restriction)) {
175262306a36Sopenharmony_ci			ret = PTR_ERR(restriction);
175362306a36Sopenharmony_ci			goto error;
175462306a36Sopenharmony_ci		}
175562306a36Sopenharmony_ci	} else {
175662306a36Sopenharmony_ci		if (_restriction)
175762306a36Sopenharmony_ci			goto error;
175862306a36Sopenharmony_ci	}
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_ci	ret = keyring_restrict(key_ref, _type ? type : NULL, restriction);
176162306a36Sopenharmony_ci	kfree(restriction);
176262306a36Sopenharmony_cierror:
176362306a36Sopenharmony_ci	key_ref_put(key_ref);
176462306a36Sopenharmony_ci	return ret;
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci#ifdef CONFIG_KEY_NOTIFICATIONS
176862306a36Sopenharmony_ci/*
176962306a36Sopenharmony_ci * Watch for changes to a key.
177062306a36Sopenharmony_ci *
177162306a36Sopenharmony_ci * The caller must have View permission to watch a key or keyring.
177262306a36Sopenharmony_ci */
177362306a36Sopenharmony_cilong keyctl_watch_key(key_serial_t id, int watch_queue_fd, int watch_id)
177462306a36Sopenharmony_ci{
177562306a36Sopenharmony_ci	struct watch_queue *wqueue;
177662306a36Sopenharmony_ci	struct watch_list *wlist = NULL;
177762306a36Sopenharmony_ci	struct watch *watch = NULL;
177862306a36Sopenharmony_ci	struct key *key;
177962306a36Sopenharmony_ci	key_ref_t key_ref;
178062306a36Sopenharmony_ci	long ret;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	if (watch_id < -1 || watch_id > 0xff)
178362306a36Sopenharmony_ci		return -EINVAL;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_NEED_VIEW);
178662306a36Sopenharmony_ci	if (IS_ERR(key_ref))
178762306a36Sopenharmony_ci		return PTR_ERR(key_ref);
178862306a36Sopenharmony_ci	key = key_ref_to_ptr(key_ref);
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	wqueue = get_watch_queue(watch_queue_fd);
179162306a36Sopenharmony_ci	if (IS_ERR(wqueue)) {
179262306a36Sopenharmony_ci		ret = PTR_ERR(wqueue);
179362306a36Sopenharmony_ci		goto err_key;
179462306a36Sopenharmony_ci	}
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_ci	if (watch_id >= 0) {
179762306a36Sopenharmony_ci		ret = -ENOMEM;
179862306a36Sopenharmony_ci		if (!key->watchers) {
179962306a36Sopenharmony_ci			wlist = kzalloc(sizeof(*wlist), GFP_KERNEL);
180062306a36Sopenharmony_ci			if (!wlist)
180162306a36Sopenharmony_ci				goto err_wqueue;
180262306a36Sopenharmony_ci			init_watch_list(wlist, NULL);
180362306a36Sopenharmony_ci		}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci		watch = kzalloc(sizeof(*watch), GFP_KERNEL);
180662306a36Sopenharmony_ci		if (!watch)
180762306a36Sopenharmony_ci			goto err_wlist;
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci		init_watch(watch, wqueue);
181062306a36Sopenharmony_ci		watch->id	= key->serial;
181162306a36Sopenharmony_ci		watch->info_id	= (u32)watch_id << WATCH_INFO_ID__SHIFT;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		ret = security_watch_key(key);
181462306a36Sopenharmony_ci		if (ret < 0)
181562306a36Sopenharmony_ci			goto err_watch;
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci		down_write(&key->sem);
181862306a36Sopenharmony_ci		if (!key->watchers) {
181962306a36Sopenharmony_ci			key->watchers = wlist;
182062306a36Sopenharmony_ci			wlist = NULL;
182162306a36Sopenharmony_ci		}
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci		ret = add_watch_to_object(watch, key->watchers);
182462306a36Sopenharmony_ci		up_write(&key->sem);
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci		if (ret == 0)
182762306a36Sopenharmony_ci			watch = NULL;
182862306a36Sopenharmony_ci	} else {
182962306a36Sopenharmony_ci		ret = -EBADSLT;
183062306a36Sopenharmony_ci		if (key->watchers) {
183162306a36Sopenharmony_ci			down_write(&key->sem);
183262306a36Sopenharmony_ci			ret = remove_watch_from_object(key->watchers,
183362306a36Sopenharmony_ci						       wqueue, key_serial(key),
183462306a36Sopenharmony_ci						       false);
183562306a36Sopenharmony_ci			up_write(&key->sem);
183662306a36Sopenharmony_ci		}
183762306a36Sopenharmony_ci	}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_cierr_watch:
184062306a36Sopenharmony_ci	kfree(watch);
184162306a36Sopenharmony_cierr_wlist:
184262306a36Sopenharmony_ci	kfree(wlist);
184362306a36Sopenharmony_cierr_wqueue:
184462306a36Sopenharmony_ci	put_watch_queue(wqueue);
184562306a36Sopenharmony_cierr_key:
184662306a36Sopenharmony_ci	key_put(key);
184762306a36Sopenharmony_ci	return ret;
184862306a36Sopenharmony_ci}
184962306a36Sopenharmony_ci#endif /* CONFIG_KEY_NOTIFICATIONS */
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci/*
185262306a36Sopenharmony_ci * Get keyrings subsystem capabilities.
185362306a36Sopenharmony_ci */
185462306a36Sopenharmony_cilong keyctl_capabilities(unsigned char __user *_buffer, size_t buflen)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	size_t size = buflen;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	if (size > 0) {
185962306a36Sopenharmony_ci		if (size > sizeof(keyrings_capabilities))
186062306a36Sopenharmony_ci			size = sizeof(keyrings_capabilities);
186162306a36Sopenharmony_ci		if (copy_to_user(_buffer, keyrings_capabilities, size) != 0)
186262306a36Sopenharmony_ci			return -EFAULT;
186362306a36Sopenharmony_ci		if (size < buflen &&
186462306a36Sopenharmony_ci		    clear_user(_buffer + size, buflen - size) != 0)
186562306a36Sopenharmony_ci			return -EFAULT;
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	return sizeof(keyrings_capabilities);
186962306a36Sopenharmony_ci}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci/*
187262306a36Sopenharmony_ci * The key control system call
187362306a36Sopenharmony_ci */
187462306a36Sopenharmony_ciSYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
187562306a36Sopenharmony_ci		unsigned long, arg4, unsigned long, arg5)
187662306a36Sopenharmony_ci{
187762306a36Sopenharmony_ci	switch (option) {
187862306a36Sopenharmony_ci	case KEYCTL_GET_KEYRING_ID:
187962306a36Sopenharmony_ci		return keyctl_get_keyring_ID((key_serial_t) arg2,
188062306a36Sopenharmony_ci					     (int) arg3);
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	case KEYCTL_JOIN_SESSION_KEYRING:
188362306a36Sopenharmony_ci		return keyctl_join_session_keyring((const char __user *) arg2);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	case KEYCTL_UPDATE:
188662306a36Sopenharmony_ci		return keyctl_update_key((key_serial_t) arg2,
188762306a36Sopenharmony_ci					 (const void __user *) arg3,
188862306a36Sopenharmony_ci					 (size_t) arg4);
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	case KEYCTL_REVOKE:
189162306a36Sopenharmony_ci		return keyctl_revoke_key((key_serial_t) arg2);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	case KEYCTL_DESCRIBE:
189462306a36Sopenharmony_ci		return keyctl_describe_key((key_serial_t) arg2,
189562306a36Sopenharmony_ci					   (char __user *) arg3,
189662306a36Sopenharmony_ci					   (unsigned) arg4);
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	case KEYCTL_CLEAR:
189962306a36Sopenharmony_ci		return keyctl_keyring_clear((key_serial_t) arg2);
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	case KEYCTL_LINK:
190262306a36Sopenharmony_ci		return keyctl_keyring_link((key_serial_t) arg2,
190362306a36Sopenharmony_ci					   (key_serial_t) arg3);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	case KEYCTL_UNLINK:
190662306a36Sopenharmony_ci		return keyctl_keyring_unlink((key_serial_t) arg2,
190762306a36Sopenharmony_ci					     (key_serial_t) arg3);
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	case KEYCTL_SEARCH:
191062306a36Sopenharmony_ci		return keyctl_keyring_search((key_serial_t) arg2,
191162306a36Sopenharmony_ci					     (const char __user *) arg3,
191262306a36Sopenharmony_ci					     (const char __user *) arg4,
191362306a36Sopenharmony_ci					     (key_serial_t) arg5);
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	case KEYCTL_READ:
191662306a36Sopenharmony_ci		return keyctl_read_key((key_serial_t) arg2,
191762306a36Sopenharmony_ci				       (char __user *) arg3,
191862306a36Sopenharmony_ci				       (size_t) arg4);
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci	case KEYCTL_CHOWN:
192162306a36Sopenharmony_ci		return keyctl_chown_key((key_serial_t) arg2,
192262306a36Sopenharmony_ci					(uid_t) arg3,
192362306a36Sopenharmony_ci					(gid_t) arg4);
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	case KEYCTL_SETPERM:
192662306a36Sopenharmony_ci		return keyctl_setperm_key((key_serial_t) arg2,
192762306a36Sopenharmony_ci					  (key_perm_t) arg3);
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ci	case KEYCTL_INSTANTIATE:
193062306a36Sopenharmony_ci		return keyctl_instantiate_key((key_serial_t) arg2,
193162306a36Sopenharmony_ci					      (const void __user *) arg3,
193262306a36Sopenharmony_ci					      (size_t) arg4,
193362306a36Sopenharmony_ci					      (key_serial_t) arg5);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	case KEYCTL_NEGATE:
193662306a36Sopenharmony_ci		return keyctl_negate_key((key_serial_t) arg2,
193762306a36Sopenharmony_ci					 (unsigned) arg3,
193862306a36Sopenharmony_ci					 (key_serial_t) arg4);
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	case KEYCTL_SET_REQKEY_KEYRING:
194162306a36Sopenharmony_ci		return keyctl_set_reqkey_keyring(arg2);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	case KEYCTL_SET_TIMEOUT:
194462306a36Sopenharmony_ci		return keyctl_set_timeout((key_serial_t) arg2,
194562306a36Sopenharmony_ci					  (unsigned) arg3);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	case KEYCTL_ASSUME_AUTHORITY:
194862306a36Sopenharmony_ci		return keyctl_assume_authority((key_serial_t) arg2);
194962306a36Sopenharmony_ci
195062306a36Sopenharmony_ci	case KEYCTL_GET_SECURITY:
195162306a36Sopenharmony_ci		return keyctl_get_security((key_serial_t) arg2,
195262306a36Sopenharmony_ci					   (char __user *) arg3,
195362306a36Sopenharmony_ci					   (size_t) arg4);
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	case KEYCTL_SESSION_TO_PARENT:
195662306a36Sopenharmony_ci		return keyctl_session_to_parent();
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	case KEYCTL_REJECT:
195962306a36Sopenharmony_ci		return keyctl_reject_key((key_serial_t) arg2,
196062306a36Sopenharmony_ci					 (unsigned) arg3,
196162306a36Sopenharmony_ci					 (unsigned) arg4,
196262306a36Sopenharmony_ci					 (key_serial_t) arg5);
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	case KEYCTL_INSTANTIATE_IOV:
196562306a36Sopenharmony_ci		return keyctl_instantiate_key_iov(
196662306a36Sopenharmony_ci			(key_serial_t) arg2,
196762306a36Sopenharmony_ci			(const struct iovec __user *) arg3,
196862306a36Sopenharmony_ci			(unsigned) arg4,
196962306a36Sopenharmony_ci			(key_serial_t) arg5);
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	case KEYCTL_INVALIDATE:
197262306a36Sopenharmony_ci		return keyctl_invalidate_key((key_serial_t) arg2);
197362306a36Sopenharmony_ci
197462306a36Sopenharmony_ci	case KEYCTL_GET_PERSISTENT:
197562306a36Sopenharmony_ci		return keyctl_get_persistent((uid_t)arg2, (key_serial_t)arg3);
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci	case KEYCTL_DH_COMPUTE:
197862306a36Sopenharmony_ci		return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2,
197962306a36Sopenharmony_ci					 (char __user *) arg3, (size_t) arg4,
198062306a36Sopenharmony_ci					 (struct keyctl_kdf_params __user *) arg5);
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	case KEYCTL_RESTRICT_KEYRING:
198362306a36Sopenharmony_ci		return keyctl_restrict_keyring((key_serial_t) arg2,
198462306a36Sopenharmony_ci					       (const char __user *) arg3,
198562306a36Sopenharmony_ci					       (const char __user *) arg4);
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	case KEYCTL_PKEY_QUERY:
198862306a36Sopenharmony_ci		if (arg3 != 0)
198962306a36Sopenharmony_ci			return -EINVAL;
199062306a36Sopenharmony_ci		return keyctl_pkey_query((key_serial_t)arg2,
199162306a36Sopenharmony_ci					 (const char __user *)arg4,
199262306a36Sopenharmony_ci					 (struct keyctl_pkey_query __user *)arg5);
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	case KEYCTL_PKEY_ENCRYPT:
199562306a36Sopenharmony_ci	case KEYCTL_PKEY_DECRYPT:
199662306a36Sopenharmony_ci	case KEYCTL_PKEY_SIGN:
199762306a36Sopenharmony_ci		return keyctl_pkey_e_d_s(
199862306a36Sopenharmony_ci			option,
199962306a36Sopenharmony_ci			(const struct keyctl_pkey_params __user *)arg2,
200062306a36Sopenharmony_ci			(const char __user *)arg3,
200162306a36Sopenharmony_ci			(const void __user *)arg4,
200262306a36Sopenharmony_ci			(void __user *)arg5);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	case KEYCTL_PKEY_VERIFY:
200562306a36Sopenharmony_ci		return keyctl_pkey_verify(
200662306a36Sopenharmony_ci			(const struct keyctl_pkey_params __user *)arg2,
200762306a36Sopenharmony_ci			(const char __user *)arg3,
200862306a36Sopenharmony_ci			(const void __user *)arg4,
200962306a36Sopenharmony_ci			(const void __user *)arg5);
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	case KEYCTL_MOVE:
201262306a36Sopenharmony_ci		return keyctl_keyring_move((key_serial_t)arg2,
201362306a36Sopenharmony_ci					   (key_serial_t)arg3,
201462306a36Sopenharmony_ci					   (key_serial_t)arg4,
201562306a36Sopenharmony_ci					   (unsigned int)arg5);
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	case KEYCTL_CAPABILITIES:
201862306a36Sopenharmony_ci		return keyctl_capabilities((unsigned char __user *)arg2, (size_t)arg3);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	case KEYCTL_WATCH_KEY:
202162306a36Sopenharmony_ci		return keyctl_watch_key((key_serial_t)arg2, (int)arg3, (int)arg4);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	default:
202462306a36Sopenharmony_ci		return -EOPNOTSUPP;
202562306a36Sopenharmony_ci	}
202662306a36Sopenharmony_ci}
2027