18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Request key authorisation token key definition.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * See Documentation/security/keys/request-key.rst
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/sched.h>
118c2ecf20Sopenharmony_ci#include <linux/err.h>
128c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci#include "internal.h"
168c2ecf20Sopenharmony_ci#include <keys/request_key_auth-type.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int request_key_auth_preparse(struct key_preparsed_payload *);
198c2ecf20Sopenharmony_cistatic void request_key_auth_free_preparse(struct key_preparsed_payload *);
208c2ecf20Sopenharmony_cistatic int request_key_auth_instantiate(struct key *,
218c2ecf20Sopenharmony_ci					struct key_preparsed_payload *);
228c2ecf20Sopenharmony_cistatic void request_key_auth_describe(const struct key *, struct seq_file *);
238c2ecf20Sopenharmony_cistatic void request_key_auth_revoke(struct key *);
248c2ecf20Sopenharmony_cistatic void request_key_auth_destroy(struct key *);
258c2ecf20Sopenharmony_cistatic long request_key_auth_read(const struct key *, char *, size_t);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * The request-key authorisation key type definition.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistruct key_type key_type_request_key_auth = {
318c2ecf20Sopenharmony_ci	.name		= ".request_key_auth",
328c2ecf20Sopenharmony_ci	.def_datalen	= sizeof(struct request_key_auth),
338c2ecf20Sopenharmony_ci	.preparse	= request_key_auth_preparse,
348c2ecf20Sopenharmony_ci	.free_preparse	= request_key_auth_free_preparse,
358c2ecf20Sopenharmony_ci	.instantiate	= request_key_auth_instantiate,
368c2ecf20Sopenharmony_ci	.describe	= request_key_auth_describe,
378c2ecf20Sopenharmony_ci	.revoke		= request_key_auth_revoke,
388c2ecf20Sopenharmony_ci	.destroy	= request_key_auth_destroy,
398c2ecf20Sopenharmony_ci	.read		= request_key_auth_read,
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic int request_key_auth_preparse(struct key_preparsed_payload *prep)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	return 0;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void request_key_auth_free_preparse(struct key_preparsed_payload *prep)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * Instantiate a request-key authorisation key.
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_cistatic int request_key_auth_instantiate(struct key *key,
558c2ecf20Sopenharmony_ci					struct key_preparsed_payload *prep)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	rcu_assign_keypointer(key, (struct request_key_auth *)prep->data);
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * Describe an authorisation token.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic void request_key_auth_describe(const struct key *key,
658c2ecf20Sopenharmony_ci				      struct seq_file *m)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct request_key_auth *rka = dereference_key_rcu(key);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (!rka)
708c2ecf20Sopenharmony_ci		return;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	seq_puts(m, "key:");
738c2ecf20Sopenharmony_ci	seq_puts(m, key->description);
748c2ecf20Sopenharmony_ci	if (key_is_positive(key))
758c2ecf20Sopenharmony_ci		seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/*
798c2ecf20Sopenharmony_ci * Read the callout_info data (retrieves the callout information).
808c2ecf20Sopenharmony_ci * - the key's semaphore is read-locked
818c2ecf20Sopenharmony_ci */
828c2ecf20Sopenharmony_cistatic long request_key_auth_read(const struct key *key,
838c2ecf20Sopenharmony_ci				  char *buffer, size_t buflen)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct request_key_auth *rka = dereference_key_locked(key);
868c2ecf20Sopenharmony_ci	size_t datalen;
878c2ecf20Sopenharmony_ci	long ret;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!rka)
908c2ecf20Sopenharmony_ci		return -EKEYREVOKED;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	datalen = rka->callout_len;
938c2ecf20Sopenharmony_ci	ret = datalen;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/* we can return the data as is */
968c2ecf20Sopenharmony_ci	if (buffer && buflen > 0) {
978c2ecf20Sopenharmony_ci		if (buflen > datalen)
988c2ecf20Sopenharmony_ci			buflen = datalen;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		memcpy(buffer, rka->callout_info, buflen);
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	return ret;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void free_request_key_auth(struct request_key_auth *rka)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	if (!rka)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci	key_put(rka->target_key);
1118c2ecf20Sopenharmony_ci	key_put(rka->dest_keyring);
1128c2ecf20Sopenharmony_ci	if (rka->cred)
1138c2ecf20Sopenharmony_ci		put_cred(rka->cred);
1148c2ecf20Sopenharmony_ci	kfree(rka->callout_info);
1158c2ecf20Sopenharmony_ci	kfree(rka);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/*
1198c2ecf20Sopenharmony_ci * Dispose of the request_key_auth record under RCU conditions
1208c2ecf20Sopenharmony_ci */
1218c2ecf20Sopenharmony_cistatic void request_key_auth_rcu_disposal(struct rcu_head *rcu)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct request_key_auth *rka =
1248c2ecf20Sopenharmony_ci		container_of(rcu, struct request_key_auth, rcu);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	free_request_key_auth(rka);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * Handle revocation of an authorisation token key.
1318c2ecf20Sopenharmony_ci *
1328c2ecf20Sopenharmony_ci * Called with the key sem write-locked.
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_cistatic void request_key_auth_revoke(struct key *key)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct request_key_auth *rka = dereference_key_locked(key);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	kenter("{%d}", key->serial);
1398c2ecf20Sopenharmony_ci	rcu_assign_keypointer(key, NULL);
1408c2ecf20Sopenharmony_ci	call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/*
1448c2ecf20Sopenharmony_ci * Destroy an instantiation authorisation token key.
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_cistatic void request_key_auth_destroy(struct key *key)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct request_key_auth *rka = rcu_access_pointer(key->payload.rcu_data0);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	kenter("{%d}", key->serial);
1518c2ecf20Sopenharmony_ci	if (rka) {
1528c2ecf20Sopenharmony_ci		rcu_assign_keypointer(key, NULL);
1538c2ecf20Sopenharmony_ci		call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * Create an authorisation token for /sbin/request-key or whoever to gain
1598c2ecf20Sopenharmony_ci * access to the caller's security data.
1608c2ecf20Sopenharmony_ci */
1618c2ecf20Sopenharmony_cistruct key *request_key_auth_new(struct key *target, const char *op,
1628c2ecf20Sopenharmony_ci				 const void *callout_info, size_t callout_len,
1638c2ecf20Sopenharmony_ci				 struct key *dest_keyring)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct request_key_auth *rka, *irka;
1668c2ecf20Sopenharmony_ci	const struct cred *cred = current_cred();
1678c2ecf20Sopenharmony_ci	struct key *authkey = NULL;
1688c2ecf20Sopenharmony_ci	char desc[20];
1698c2ecf20Sopenharmony_ci	int ret = -ENOMEM;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	kenter("%d,", target->serial);
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	/* allocate a auth record */
1748c2ecf20Sopenharmony_ci	rka = kzalloc(sizeof(*rka), GFP_KERNEL);
1758c2ecf20Sopenharmony_ci	if (!rka)
1768c2ecf20Sopenharmony_ci		goto error;
1778c2ecf20Sopenharmony_ci	rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
1788c2ecf20Sopenharmony_ci	if (!rka->callout_info)
1798c2ecf20Sopenharmony_ci		goto error_free_rka;
1808c2ecf20Sopenharmony_ci	rka->callout_len = callout_len;
1818c2ecf20Sopenharmony_ci	strlcpy(rka->op, op, sizeof(rka->op));
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* see if the calling process is already servicing the key request of
1848c2ecf20Sopenharmony_ci	 * another process */
1858c2ecf20Sopenharmony_ci	if (cred->request_key_auth) {
1868c2ecf20Sopenharmony_ci		/* it is - use that instantiation context here too */
1878c2ecf20Sopenharmony_ci		down_read(&cred->request_key_auth->sem);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		/* if the auth key has been revoked, then the key we're
1908c2ecf20Sopenharmony_ci		 * servicing is already instantiated */
1918c2ecf20Sopenharmony_ci		if (test_bit(KEY_FLAG_REVOKED,
1928c2ecf20Sopenharmony_ci			     &cred->request_key_auth->flags)) {
1938c2ecf20Sopenharmony_ci			up_read(&cred->request_key_auth->sem);
1948c2ecf20Sopenharmony_ci			ret = -EKEYREVOKED;
1958c2ecf20Sopenharmony_ci			goto error_free_rka;
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci		irka = cred->request_key_auth->payload.data[0];
1998c2ecf20Sopenharmony_ci		rka->cred = get_cred(irka->cred);
2008c2ecf20Sopenharmony_ci		rka->pid = irka->pid;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		up_read(&cred->request_key_auth->sem);
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci	else {
2058c2ecf20Sopenharmony_ci		/* it isn't - use this process as the context */
2068c2ecf20Sopenharmony_ci		rka->cred = get_cred(cred);
2078c2ecf20Sopenharmony_ci		rka->pid = current->pid;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	rka->target_key = key_get(target);
2118c2ecf20Sopenharmony_ci	rka->dest_keyring = key_get(dest_keyring);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* allocate the auth key */
2148c2ecf20Sopenharmony_ci	sprintf(desc, "%x", target->serial);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	authkey = key_alloc(&key_type_request_key_auth, desc,
2178c2ecf20Sopenharmony_ci			    cred->fsuid, cred->fsgid, cred,
2188c2ecf20Sopenharmony_ci			    KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH | KEY_POS_LINK |
2198c2ecf20Sopenharmony_ci			    KEY_USR_VIEW, KEY_ALLOC_NOT_IN_QUOTA, NULL);
2208c2ecf20Sopenharmony_ci	if (IS_ERR(authkey)) {
2218c2ecf20Sopenharmony_ci		ret = PTR_ERR(authkey);
2228c2ecf20Sopenharmony_ci		goto error_free_rka;
2238c2ecf20Sopenharmony_ci	}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* construct the auth key */
2268c2ecf20Sopenharmony_ci	ret = key_instantiate_and_link(authkey, rka, 0, NULL, NULL);
2278c2ecf20Sopenharmony_ci	if (ret < 0)
2288c2ecf20Sopenharmony_ci		goto error_put_authkey;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage));
2318c2ecf20Sopenharmony_ci	return authkey;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cierror_put_authkey:
2348c2ecf20Sopenharmony_ci	key_put(authkey);
2358c2ecf20Sopenharmony_cierror_free_rka:
2368c2ecf20Sopenharmony_ci	free_request_key_auth(rka);
2378c2ecf20Sopenharmony_cierror:
2388c2ecf20Sopenharmony_ci	kleave("= %d", ret);
2398c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/*
2438c2ecf20Sopenharmony_ci * Search the current process's keyrings for the authorisation key for
2448c2ecf20Sopenharmony_ci * instantiation of a key.
2458c2ecf20Sopenharmony_ci */
2468c2ecf20Sopenharmony_cistruct key *key_get_instantiation_authkey(key_serial_t target_id)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	char description[16];
2498c2ecf20Sopenharmony_ci	struct keyring_search_context ctx = {
2508c2ecf20Sopenharmony_ci		.index_key.type		= &key_type_request_key_auth,
2518c2ecf20Sopenharmony_ci		.index_key.description	= description,
2528c2ecf20Sopenharmony_ci		.cred			= current_cred(),
2538c2ecf20Sopenharmony_ci		.match_data.cmp		= key_default_cmp,
2548c2ecf20Sopenharmony_ci		.match_data.raw_data	= description,
2558c2ecf20Sopenharmony_ci		.match_data.lookup_type	= KEYRING_SEARCH_LOOKUP_DIRECT,
2568c2ecf20Sopenharmony_ci		.flags			= (KEYRING_SEARCH_DO_STATE_CHECK |
2578c2ecf20Sopenharmony_ci					   KEYRING_SEARCH_RECURSE),
2588c2ecf20Sopenharmony_ci	};
2598c2ecf20Sopenharmony_ci	struct key *authkey;
2608c2ecf20Sopenharmony_ci	key_ref_t authkey_ref;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	ctx.index_key.desc_len = sprintf(description, "%x", target_id);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	rcu_read_lock();
2658c2ecf20Sopenharmony_ci	authkey_ref = search_process_keyrings_rcu(&ctx);
2668c2ecf20Sopenharmony_ci	rcu_read_unlock();
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (IS_ERR(authkey_ref)) {
2698c2ecf20Sopenharmony_ci		authkey = ERR_CAST(authkey_ref);
2708c2ecf20Sopenharmony_ci		if (authkey == ERR_PTR(-EAGAIN))
2718c2ecf20Sopenharmony_ci			authkey = ERR_PTR(-ENOKEY);
2728c2ecf20Sopenharmony_ci		goto error;
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	authkey = key_ref_to_ptr(authkey_ref);
2768c2ecf20Sopenharmony_ci	if (test_bit(KEY_FLAG_REVOKED, &authkey->flags)) {
2778c2ecf20Sopenharmony_ci		key_put(authkey);
2788c2ecf20Sopenharmony_ci		authkey = ERR_PTR(-EKEYREVOKED);
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cierror:
2828c2ecf20Sopenharmony_ci	return authkey;
2838c2ecf20Sopenharmony_ci}
284