18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* user_defined.c: user defined key type
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
58c2ecf20Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/export.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
128c2ecf20Sopenharmony_ci#include <linux/err.h>
138c2ecf20Sopenharmony_ci#include <keys/user-type.h>
148c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
158c2ecf20Sopenharmony_ci#include "internal.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic int logon_vet_description(const char *desc);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * user defined keys take an arbitrary string as the description and an
218c2ecf20Sopenharmony_ci * arbitrary blob of data as the payload
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_cistruct key_type key_type_user = {
248c2ecf20Sopenharmony_ci	.name			= "user",
258c2ecf20Sopenharmony_ci	.preparse		= user_preparse,
268c2ecf20Sopenharmony_ci	.free_preparse		= user_free_preparse,
278c2ecf20Sopenharmony_ci	.instantiate		= generic_key_instantiate,
288c2ecf20Sopenharmony_ci	.update			= user_update,
298c2ecf20Sopenharmony_ci	.revoke			= user_revoke,
308c2ecf20Sopenharmony_ci	.destroy		= user_destroy,
318c2ecf20Sopenharmony_ci	.describe		= user_describe,
328c2ecf20Sopenharmony_ci	.read			= user_read,
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(key_type_user);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * This key type is essentially the same as key_type_user, but it does
398c2ecf20Sopenharmony_ci * not define a .read op. This is suitable for storing username and
408c2ecf20Sopenharmony_ci * password pairs in the keyring that you do not want to be readable
418c2ecf20Sopenharmony_ci * from userspace.
428c2ecf20Sopenharmony_ci */
438c2ecf20Sopenharmony_cistruct key_type key_type_logon = {
448c2ecf20Sopenharmony_ci	.name			= "logon",
458c2ecf20Sopenharmony_ci	.preparse		= user_preparse,
468c2ecf20Sopenharmony_ci	.free_preparse		= user_free_preparse,
478c2ecf20Sopenharmony_ci	.instantiate		= generic_key_instantiate,
488c2ecf20Sopenharmony_ci	.update			= user_update,
498c2ecf20Sopenharmony_ci	.revoke			= user_revoke,
508c2ecf20Sopenharmony_ci	.destroy		= user_destroy,
518c2ecf20Sopenharmony_ci	.describe		= user_describe,
528c2ecf20Sopenharmony_ci	.vet_description	= logon_vet_description,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(key_type_logon);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Preparse a user defined key payload
588c2ecf20Sopenharmony_ci */
598c2ecf20Sopenharmony_ciint user_preparse(struct key_preparsed_payload *prep)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct user_key_payload *upayload;
628c2ecf20Sopenharmony_ci	size_t datalen = prep->datalen;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (datalen <= 0 || datalen > 32767 || !prep->data)
658c2ecf20Sopenharmony_ci		return -EINVAL;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
688c2ecf20Sopenharmony_ci	if (!upayload)
698c2ecf20Sopenharmony_ci		return -ENOMEM;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* attach the data */
728c2ecf20Sopenharmony_ci	prep->quotalen = datalen;
738c2ecf20Sopenharmony_ci	prep->payload.data[0] = upayload;
748c2ecf20Sopenharmony_ci	upayload->datalen = datalen;
758c2ecf20Sopenharmony_ci	memcpy(upayload->data, prep->data, datalen);
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(user_preparse);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Free a preparse of a user defined key payload
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_civoid user_free_preparse(struct key_preparsed_payload *prep)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	kfree_sensitive(prep->payload.data[0]);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(user_free_preparse);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic void user_free_payload_rcu(struct rcu_head *head)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	struct user_key_payload *payload;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	payload = container_of(head, struct user_key_payload, rcu);
948c2ecf20Sopenharmony_ci	kfree_sensitive(payload);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci/*
988c2ecf20Sopenharmony_ci * update a user defined key
998c2ecf20Sopenharmony_ci * - the key's semaphore is write-locked
1008c2ecf20Sopenharmony_ci */
1018c2ecf20Sopenharmony_ciint user_update(struct key *key, struct key_preparsed_payload *prep)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct user_key_payload *zap = NULL;
1048c2ecf20Sopenharmony_ci	int ret;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* check the quota and attach the new data */
1078c2ecf20Sopenharmony_ci	ret = key_payload_reserve(key, prep->datalen);
1088c2ecf20Sopenharmony_ci	if (ret < 0)
1098c2ecf20Sopenharmony_ci		return ret;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	/* attach the new data, displacing the old */
1128c2ecf20Sopenharmony_ci	key->expiry = prep->expiry;
1138c2ecf20Sopenharmony_ci	if (key_is_positive(key))
1148c2ecf20Sopenharmony_ci		zap = dereference_key_locked(key);
1158c2ecf20Sopenharmony_ci	rcu_assign_keypointer(key, prep->payload.data[0]);
1168c2ecf20Sopenharmony_ci	prep->payload.data[0] = NULL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (zap)
1198c2ecf20Sopenharmony_ci		call_rcu(&zap->rcu, user_free_payload_rcu);
1208c2ecf20Sopenharmony_ci	return ret;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(user_update);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci/*
1258c2ecf20Sopenharmony_ci * dispose of the links from a revoked keyring
1268c2ecf20Sopenharmony_ci * - called with the key sem write-locked
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_civoid user_revoke(struct key *key)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	struct user_key_payload *upayload = user_key_payload_locked(key);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* clear the quota */
1338c2ecf20Sopenharmony_ci	key_payload_reserve(key, 0);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	if (upayload) {
1368c2ecf20Sopenharmony_ci		rcu_assign_keypointer(key, NULL);
1378c2ecf20Sopenharmony_ci		call_rcu(&upayload->rcu, user_free_payload_rcu);
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(user_revoke);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/*
1448c2ecf20Sopenharmony_ci * dispose of the data dangling from the corpse of a user key
1458c2ecf20Sopenharmony_ci */
1468c2ecf20Sopenharmony_civoid user_destroy(struct key *key)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct user_key_payload *upayload = key->payload.data[0];
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	kfree_sensitive(upayload);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(user_destroy);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/*
1568c2ecf20Sopenharmony_ci * describe the user key
1578c2ecf20Sopenharmony_ci */
1588c2ecf20Sopenharmony_civoid user_describe(const struct key *key, struct seq_file *m)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	seq_puts(m, key->description);
1618c2ecf20Sopenharmony_ci	if (key_is_positive(key))
1628c2ecf20Sopenharmony_ci		seq_printf(m, ": %u", key->datalen);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(user_describe);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/*
1688c2ecf20Sopenharmony_ci * read the key data
1698c2ecf20Sopenharmony_ci * - the key's semaphore is read-locked
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cilong user_read(const struct key *key, char *buffer, size_t buflen)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	const struct user_key_payload *upayload;
1748c2ecf20Sopenharmony_ci	long ret;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	upayload = user_key_payload_locked(key);
1778c2ecf20Sopenharmony_ci	ret = upayload->datalen;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* we can return the data as is */
1808c2ecf20Sopenharmony_ci	if (buffer && buflen > 0) {
1818c2ecf20Sopenharmony_ci		if (buflen > upayload->datalen)
1828c2ecf20Sopenharmony_ci			buflen = upayload->datalen;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		memcpy(buffer, upayload->data, buflen);
1858c2ecf20Sopenharmony_ci	}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return ret;
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(user_read);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/* Vet the description for a "logon" key */
1938c2ecf20Sopenharmony_cistatic int logon_vet_description(const char *desc)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	char *p;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* require a "qualified" description string */
1988c2ecf20Sopenharmony_ci	p = strchr(desc, ':');
1998c2ecf20Sopenharmony_ci	if (!p)
2008c2ecf20Sopenharmony_ci		return -EINVAL;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/* also reject description with ':' as first char */
2038c2ecf20Sopenharmony_ci	if (p == desc)
2048c2ecf20Sopenharmony_ci		return -EINVAL;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
208