162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* Request key authorisation token key definition.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * See Documentation/security/keys/request-key.rst
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/sched.h>
1162306a36Sopenharmony_ci#include <linux/err.h>
1262306a36Sopenharmony_ci#include <linux/seq_file.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include "internal.h"
1662306a36Sopenharmony_ci#include <keys/request_key_auth-type.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int request_key_auth_preparse(struct key_preparsed_payload *);
1962306a36Sopenharmony_cistatic void request_key_auth_free_preparse(struct key_preparsed_payload *);
2062306a36Sopenharmony_cistatic int request_key_auth_instantiate(struct key *,
2162306a36Sopenharmony_ci					struct key_preparsed_payload *);
2262306a36Sopenharmony_cistatic void request_key_auth_describe(const struct key *, struct seq_file *);
2362306a36Sopenharmony_cistatic void request_key_auth_revoke(struct key *);
2462306a36Sopenharmony_cistatic void request_key_auth_destroy(struct key *);
2562306a36Sopenharmony_cistatic long request_key_auth_read(const struct key *, char *, size_t);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/*
2862306a36Sopenharmony_ci * The request-key authorisation key type definition.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistruct key_type key_type_request_key_auth = {
3162306a36Sopenharmony_ci	.name		= ".request_key_auth",
3262306a36Sopenharmony_ci	.def_datalen	= sizeof(struct request_key_auth),
3362306a36Sopenharmony_ci	.preparse	= request_key_auth_preparse,
3462306a36Sopenharmony_ci	.free_preparse	= request_key_auth_free_preparse,
3562306a36Sopenharmony_ci	.instantiate	= request_key_auth_instantiate,
3662306a36Sopenharmony_ci	.describe	= request_key_auth_describe,
3762306a36Sopenharmony_ci	.revoke		= request_key_auth_revoke,
3862306a36Sopenharmony_ci	.destroy	= request_key_auth_destroy,
3962306a36Sopenharmony_ci	.read		= request_key_auth_read,
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int request_key_auth_preparse(struct key_preparsed_payload *prep)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * Instantiate a request-key authorisation key.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic int request_key_auth_instantiate(struct key *key,
5562306a36Sopenharmony_ci					struct key_preparsed_payload *prep)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	rcu_assign_keypointer(key, (struct request_key_auth *)prep->data);
5862306a36Sopenharmony_ci	return 0;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/*
6262306a36Sopenharmony_ci * Describe an authorisation token.
6362306a36Sopenharmony_ci */
6462306a36Sopenharmony_cistatic void request_key_auth_describe(const struct key *key,
6562306a36Sopenharmony_ci				      struct seq_file *m)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	struct request_key_auth *rka = dereference_key_rcu(key);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (!rka)
7062306a36Sopenharmony_ci		return;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	seq_puts(m, "key:");
7362306a36Sopenharmony_ci	seq_puts(m, key->description);
7462306a36Sopenharmony_ci	if (key_is_positive(key))
7562306a36Sopenharmony_ci		seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/*
7962306a36Sopenharmony_ci * Read the callout_info data (retrieves the callout information).
8062306a36Sopenharmony_ci * - the key's semaphore is read-locked
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic long request_key_auth_read(const struct key *key,
8362306a36Sopenharmony_ci				  char *buffer, size_t buflen)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct request_key_auth *rka = dereference_key_locked(key);
8662306a36Sopenharmony_ci	size_t datalen;
8762306a36Sopenharmony_ci	long ret;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	if (!rka)
9062306a36Sopenharmony_ci		return -EKEYREVOKED;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	datalen = rka->callout_len;
9362306a36Sopenharmony_ci	ret = datalen;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* we can return the data as is */
9662306a36Sopenharmony_ci	if (buffer && buflen > 0) {
9762306a36Sopenharmony_ci		if (buflen > datalen)
9862306a36Sopenharmony_ci			buflen = datalen;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		memcpy(buffer, rka->callout_info, buflen);
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return ret;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void free_request_key_auth(struct request_key_auth *rka)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	if (!rka)
10962306a36Sopenharmony_ci		return;
11062306a36Sopenharmony_ci	key_put(rka->target_key);
11162306a36Sopenharmony_ci	key_put(rka->dest_keyring);
11262306a36Sopenharmony_ci	if (rka->cred)
11362306a36Sopenharmony_ci		put_cred(rka->cred);
11462306a36Sopenharmony_ci	kfree(rka->callout_info);
11562306a36Sopenharmony_ci	kfree(rka);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/*
11962306a36Sopenharmony_ci * Dispose of the request_key_auth record under RCU conditions
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic void request_key_auth_rcu_disposal(struct rcu_head *rcu)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	struct request_key_auth *rka =
12462306a36Sopenharmony_ci		container_of(rcu, struct request_key_auth, rcu);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	free_request_key_auth(rka);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Handle revocation of an authorisation token key.
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * Called with the key sem write-locked.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistatic void request_key_auth_revoke(struct key *key)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct request_key_auth *rka = dereference_key_locked(key);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	kenter("{%d}", key->serial);
13962306a36Sopenharmony_ci	rcu_assign_keypointer(key, NULL);
14062306a36Sopenharmony_ci	call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * Destroy an instantiation authorisation token key.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic void request_key_auth_destroy(struct key *key)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct request_key_auth *rka = rcu_access_pointer(key->payload.rcu_data0);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	kenter("{%d}", key->serial);
15162306a36Sopenharmony_ci	if (rka) {
15262306a36Sopenharmony_ci		rcu_assign_keypointer(key, NULL);
15362306a36Sopenharmony_ci		call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci/*
15862306a36Sopenharmony_ci * Create an authorisation token for /sbin/request-key or whoever to gain
15962306a36Sopenharmony_ci * access to the caller's security data.
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_cistruct key *request_key_auth_new(struct key *target, const char *op,
16262306a36Sopenharmony_ci				 const void *callout_info, size_t callout_len,
16362306a36Sopenharmony_ci				 struct key *dest_keyring)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct request_key_auth *rka, *irka;
16662306a36Sopenharmony_ci	const struct cred *cred = current_cred();
16762306a36Sopenharmony_ci	struct key *authkey = NULL;
16862306a36Sopenharmony_ci	char desc[20];
16962306a36Sopenharmony_ci	int ret = -ENOMEM;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	kenter("%d,", target->serial);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* allocate a auth record */
17462306a36Sopenharmony_ci	rka = kzalloc(sizeof(*rka), GFP_KERNEL);
17562306a36Sopenharmony_ci	if (!rka)
17662306a36Sopenharmony_ci		goto error;
17762306a36Sopenharmony_ci	rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
17862306a36Sopenharmony_ci	if (!rka->callout_info)
17962306a36Sopenharmony_ci		goto error_free_rka;
18062306a36Sopenharmony_ci	rka->callout_len = callout_len;
18162306a36Sopenharmony_ci	strscpy(rka->op, op, sizeof(rka->op));
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* see if the calling process is already servicing the key request of
18462306a36Sopenharmony_ci	 * another process */
18562306a36Sopenharmony_ci	if (cred->request_key_auth) {
18662306a36Sopenharmony_ci		/* it is - use that instantiation context here too */
18762306a36Sopenharmony_ci		down_read(&cred->request_key_auth->sem);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		/* if the auth key has been revoked, then the key we're
19062306a36Sopenharmony_ci		 * servicing is already instantiated */
19162306a36Sopenharmony_ci		if (test_bit(KEY_FLAG_REVOKED,
19262306a36Sopenharmony_ci			     &cred->request_key_auth->flags)) {
19362306a36Sopenharmony_ci			up_read(&cred->request_key_auth->sem);
19462306a36Sopenharmony_ci			ret = -EKEYREVOKED;
19562306a36Sopenharmony_ci			goto error_free_rka;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		irka = cred->request_key_auth->payload.data[0];
19962306a36Sopenharmony_ci		rka->cred = get_cred(irka->cred);
20062306a36Sopenharmony_ci		rka->pid = irka->pid;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		up_read(&cred->request_key_auth->sem);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	else {
20562306a36Sopenharmony_ci		/* it isn't - use this process as the context */
20662306a36Sopenharmony_ci		rka->cred = get_cred(cred);
20762306a36Sopenharmony_ci		rka->pid = current->pid;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	rka->target_key = key_get(target);
21162306a36Sopenharmony_ci	rka->dest_keyring = key_get(dest_keyring);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* allocate the auth key */
21462306a36Sopenharmony_ci	sprintf(desc, "%x", target->serial);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	authkey = key_alloc(&key_type_request_key_auth, desc,
21762306a36Sopenharmony_ci			    cred->fsuid, cred->fsgid, cred,
21862306a36Sopenharmony_ci			    KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK |
21962306a36Sopenharmony_ci			    KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
22062306a36Sopenharmony_ci	if (IS_ERR(authkey)) {
22162306a36Sopenharmony_ci		ret = PTR_ERR(authkey);
22262306a36Sopenharmony_ci		goto error_free_rka;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	/* construct the auth key */
22662306a36Sopenharmony_ci	ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
22762306a36Sopenharmony_ci	if (ret < 0)
22862306a36Sopenharmony_ci		goto error_put_authkey;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
23162306a36Sopenharmony_ci	return authkey;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cierror_put_authkey:
23462306a36Sopenharmony_ci	key_put(authkey);
23562306a36Sopenharmony_cierror_free_rka:
23662306a36Sopenharmony_ci	free_request_key_auth(rka);
23762306a36Sopenharmony_cierror:
23862306a36Sopenharmony_ci	kleave("= %d", ret);
23962306a36Sopenharmony_ci	return ERR_PTR(ret);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * Search the current process's keyrings for the authorisation key for
24462306a36Sopenharmony_ci * instantiation of a key.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_cistruct key *key_get_instantiation_authkey(key_serial_t target_id)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	char description[16];
24962306a36Sopenharmony_ci	struct keyring_search_context ctx = {
25062306a36Sopenharmony_ci		.index_key.type		= &key_type_request_key_auth,
25162306a36Sopenharmony_ci		.index_key.description	= description,
25262306a36Sopenharmony_ci		.cred			= current_cred(),
25362306a36Sopenharmony_ci		.match_data.cmp		= key_default_cmp,
25462306a36Sopenharmony_ci		.match_data.raw_data	= description,
25562306a36Sopenharmony_ci		.match_data.lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,
25662306a36Sopenharmony_ci		.flags			= (KEYRING_SEARCH_DO_STATE_CHECK |
25762306a36Sopenharmony_ci					   KEYRING_SEARCH_RECURSE),
25862306a36Sopenharmony_ci	};
25962306a36Sopenharmony_ci	struct key *authkey;
26062306a36Sopenharmony_ci	key_ref_t authkey_ref;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ctx.index_key.desc_len = sprintf(description, "%x", target_id);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	rcu_read_lock();
26562306a36Sopenharmony_ci	authkey_ref = search_process_keyrings_rcu(&ctx);
26662306a36Sopenharmony_ci	rcu_read_unlock();
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if (IS_ERR(authkey_ref)) {
26962306a36Sopenharmony_ci		authkey = ERR_CAST(authkey_ref);
27062306a36Sopenharmony_ci		if (authkey == ERR_PTR(-EAGAIN))
27162306a36Sopenharmony_ci			authkey = ERR_PTR(-ENOKEY);
27262306a36Sopenharmony_ci		goto error;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	authkey = key_ref_to_ptr(authkey_ref);
27662306a36Sopenharmony_ci	if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
27762306a36Sopenharmony_ci		key_put(authkey);
27862306a36Sopenharmony_ci		authkey = ERR_PTR(-EKEYREVOKED);
27962306a36Sopenharmony_ci	}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cierror:
28262306a36Sopenharmony_ci	return authkey;
28362306a36Sopenharmony_ci}
284