162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* user_defined.c: user defined key type
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/export.h>
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/seq_file.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <keys/user-type.h>
1462306a36Sopenharmony_ci#include <linux/uaccess.h>
1562306a36Sopenharmony_ci#include "internal.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic int logon_vet_description(const char *desc);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * user defined keys take an arbitrary string as the description and an
2162306a36Sopenharmony_ci * arbitrary blob of data as the payload
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_cistruct key_type key_type_user = {
2462306a36Sopenharmony_ci	.name			= "user",
2562306a36Sopenharmony_ci	.preparse		= user_preparse,
2662306a36Sopenharmony_ci	.free_preparse		= user_free_preparse,
2762306a36Sopenharmony_ci	.instantiate		= generic_key_instantiate,
2862306a36Sopenharmony_ci	.update			= user_update,
2962306a36Sopenharmony_ci	.revoke			= user_revoke,
3062306a36Sopenharmony_ci	.destroy		= user_destroy,
3162306a36Sopenharmony_ci	.describe		= user_describe,
3262306a36Sopenharmony_ci	.read			= user_read,
3362306a36Sopenharmony_ci};
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(key_type_user);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * This key type is essentially the same as key_type_user, but it does
3962306a36Sopenharmony_ci * not define a .read op. This is suitable for storing username and
4062306a36Sopenharmony_ci * password pairs in the keyring that you do not want to be readable
4162306a36Sopenharmony_ci * from userspace.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistruct key_type key_type_logon = {
4462306a36Sopenharmony_ci	.name			= "logon",
4562306a36Sopenharmony_ci	.preparse		= user_preparse,
4662306a36Sopenharmony_ci	.free_preparse		= user_free_preparse,
4762306a36Sopenharmony_ci	.instantiate		= generic_key_instantiate,
4862306a36Sopenharmony_ci	.update			= user_update,
4962306a36Sopenharmony_ci	.revoke			= user_revoke,
5062306a36Sopenharmony_ci	.destroy		= user_destroy,
5162306a36Sopenharmony_ci	.describe		= user_describe,
5262306a36Sopenharmony_ci	.vet_description	= logon_vet_description,
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(key_type_logon);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * Preparse a user defined key payload
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_ciint user_preparse(struct key_preparsed_payload *prep)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct user_key_payload *upayload;
6262306a36Sopenharmony_ci	size_t datalen = prep->datalen;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (datalen <= 0 || datalen > 32767 || !prep->data)
6562306a36Sopenharmony_ci		return -EINVAL;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL);
6862306a36Sopenharmony_ci	if (!upayload)
6962306a36Sopenharmony_ci		return -ENOMEM;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	/* attach the data */
7262306a36Sopenharmony_ci	prep->quotalen = datalen;
7362306a36Sopenharmony_ci	prep->payload.data[0] = upayload;
7462306a36Sopenharmony_ci	upayload->datalen = datalen;
7562306a36Sopenharmony_ci	memcpy(upayload->data, prep->data, datalen);
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(user_preparse);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * Free a preparse of a user defined key payload
8262306a36Sopenharmony_ci */
8362306a36Sopenharmony_civoid user_free_preparse(struct key_preparsed_payload *prep)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	kfree_sensitive(prep->payload.data[0]);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(user_free_preparse);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void user_free_payload_rcu(struct rcu_head *head)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct user_key_payload *payload;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	payload = container_of(head, struct user_key_payload, rcu);
9462306a36Sopenharmony_ci	kfree_sensitive(payload);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * update a user defined key
9962306a36Sopenharmony_ci * - the key's semaphore is write-locked
10062306a36Sopenharmony_ci */
10162306a36Sopenharmony_ciint user_update(struct key *key, struct key_preparsed_payload *prep)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct user_key_payload *zap = NULL;
10462306a36Sopenharmony_ci	int ret;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	/* check the quota and attach the new data */
10762306a36Sopenharmony_ci	ret = key_payload_reserve(key, prep->datalen);
10862306a36Sopenharmony_ci	if (ret < 0)
10962306a36Sopenharmony_ci		return ret;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* attach the new data, displacing the old */
11262306a36Sopenharmony_ci	key->expiry = prep->expiry;
11362306a36Sopenharmony_ci	if (key_is_positive(key))
11462306a36Sopenharmony_ci		zap = dereference_key_locked(key);
11562306a36Sopenharmony_ci	rcu_assign_keypointer(key, prep->payload.data[0]);
11662306a36Sopenharmony_ci	prep->payload.data[0] = NULL;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (zap)
11962306a36Sopenharmony_ci		call_rcu(&zap->rcu, user_free_payload_rcu);
12062306a36Sopenharmony_ci	return ret;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(user_update);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/*
12562306a36Sopenharmony_ci * dispose of the links from a revoked keyring
12662306a36Sopenharmony_ci * - called with the key sem write-locked
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_civoid user_revoke(struct key *key)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct user_key_payload *upayload = user_key_payload_locked(key);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* clear the quota */
13362306a36Sopenharmony_ci	key_payload_reserve(key, 0);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (upayload) {
13662306a36Sopenharmony_ci		rcu_assign_keypointer(key, NULL);
13762306a36Sopenharmony_ci		call_rcu(&upayload->rcu, user_free_payload_rcu);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ciEXPORT_SYMBOL(user_revoke);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * dispose of the data dangling from the corpse of a user key
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_civoid user_destroy(struct key *key)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct user_key_payload *upayload = key->payload.data[0];
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	kfree_sensitive(upayload);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(user_destroy);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci/*
15662306a36Sopenharmony_ci * describe the user key
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_civoid user_describe(const struct key *key, struct seq_file *m)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	seq_puts(m, key->description);
16162306a36Sopenharmony_ci	if (key_is_positive(key))
16262306a36Sopenharmony_ci		seq_printf(m, ": %u", key->datalen);
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(user_describe);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/*
16862306a36Sopenharmony_ci * read the key data
16962306a36Sopenharmony_ci * - the key's semaphore is read-locked
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_cilong user_read(const struct key *key, char *buffer, size_t buflen)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	const struct user_key_payload *upayload;
17462306a36Sopenharmony_ci	long ret;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	upayload = user_key_payload_locked(key);
17762306a36Sopenharmony_ci	ret = upayload->datalen;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/* we can return the data as is */
18062306a36Sopenharmony_ci	if (buffer && buflen > 0) {
18162306a36Sopenharmony_ci		if (buflen > upayload->datalen)
18262306a36Sopenharmony_ci			buflen = upayload->datalen;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		memcpy(buffer, upayload->data, buflen);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	return ret;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(user_read);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/* Vet the description for a "logon" key */
19362306a36Sopenharmony_cistatic int logon_vet_description(const char *desc)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	char *p;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* require a "qualified" description string */
19862306a36Sopenharmony_ci	p = strchr(desc, ':');
19962306a36Sopenharmony_ci	if (!p)
20062306a36Sopenharmony_ci		return -EINVAL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	/* also reject description with ':' as first char */
20362306a36Sopenharmony_ci	if (p == desc)
20462306a36Sopenharmony_ci		return -EINVAL;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
208