cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

big_key.c (6695B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* Large capacity key type
      3 *
      4 * Copyright (C) 2017-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
      5 * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
      6 * Written by David Howells (dhowells@redhat.com)
      7 */
      8
      9#define pr_fmt(fmt) "big_key: "fmt
     10#include <linux/init.h>
     11#include <linux/seq_file.h>
     12#include <linux/file.h>
     13#include <linux/shmem_fs.h>
     14#include <linux/err.h>
     15#include <linux/random.h>
     16#include <keys/user-type.h>
     17#include <keys/big_key-type.h>
     18#include <crypto/chacha20poly1305.h>
     19
     20/*
     21 * Layout of key payload words.
     22 */
     23struct big_key_payload {
     24	u8 *data;
     25	struct path path;
     26	size_t length;
     27};
     28#define to_big_key_payload(payload)			\
     29	(struct big_key_payload *)((payload).data)
     30
     31/*
     32 * If the data is under this limit, there's no point creating a shm file to
     33 * hold it as the permanently resident metadata for the shmem fs will be at
     34 * least as large as the data.
     35 */
     36#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
     37
     38/*
     39 * big_key defined keys take an arbitrary string as the description and an
     40 * arbitrary blob of data as the payload
     41 */
     42struct key_type key_type_big_key = {
     43	.name			= "big_key",
     44	.preparse		= big_key_preparse,
     45	.free_preparse		= big_key_free_preparse,
     46	.instantiate		= generic_key_instantiate,
     47	.revoke			= big_key_revoke,
     48	.destroy		= big_key_destroy,
     49	.describe		= big_key_describe,
     50	.read			= big_key_read,
     51	.update			= big_key_update,
     52};
     53
     54/*
     55 * Preparse a big key
     56 */
     57int big_key_preparse(struct key_preparsed_payload *prep)
     58{
     59	struct big_key_payload *payload = to_big_key_payload(prep->payload);
     60	struct file *file;
     61	u8 *buf, *enckey;
     62	ssize_t written;
     63	size_t datalen = prep->datalen;
     64	size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
     65	int ret;
     66
     67	BUILD_BUG_ON(sizeof(*payload) != sizeof(prep->payload.data));
     68
     69	if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data)
     70		return -EINVAL;
     71
     72	/* Set an arbitrary quota */
     73	prep->quotalen = 16;
     74
     75	payload->length = datalen;
     76
     77	if (datalen > BIG_KEY_FILE_THRESHOLD) {
     78		/* Create a shmem file to store the data in.  This will permit the data
     79		 * to be swapped out if needed.
     80		 *
     81		 * File content is stored encrypted with randomly generated key.
     82		 * Since the key is random for each file, we can set the nonce
     83		 * to zero, provided we never define a ->update() call.
     84		 */
     85		loff_t pos = 0;
     86
     87		buf = kvmalloc(enclen, GFP_KERNEL);
     88		if (!buf)
     89			return -ENOMEM;
     90
     91		/* generate random key */
     92		enckey = kmalloc(CHACHA20POLY1305_KEY_SIZE, GFP_KERNEL);
     93		if (!enckey) {
     94			ret = -ENOMEM;
     95			goto error;
     96		}
     97		ret = get_random_bytes_wait(enckey, CHACHA20POLY1305_KEY_SIZE);
     98		if (unlikely(ret))
     99			goto err_enckey;
    100
    101		/* encrypt data */
    102		chacha20poly1305_encrypt(buf, prep->data, datalen, NULL, 0,
    103					 0, enckey);
    104
    105		/* save aligned data to file */
    106		file = shmem_kernel_file_setup("", enclen, 0);
    107		if (IS_ERR(file)) {
    108			ret = PTR_ERR(file);
    109			goto err_enckey;
    110		}
    111
    112		written = kernel_write(file, buf, enclen, &pos);
    113		if (written != enclen) {
    114			ret = written;
    115			if (written >= 0)
    116				ret = -EIO;
    117			goto err_fput;
    118		}
    119
    120		/* Pin the mount and dentry to the key so that we can open it again
    121		 * later
    122		 */
    123		payload->data = enckey;
    124		payload->path = file->f_path;
    125		path_get(&payload->path);
    126		fput(file);
    127		kvfree_sensitive(buf, enclen);
    128	} else {
    129		/* Just store the data in a buffer */
    130		void *data = kmalloc(datalen, GFP_KERNEL);
    131
    132		if (!data)
    133			return -ENOMEM;
    134
    135		payload->data = data;
    136		memcpy(data, prep->data, prep->datalen);
    137	}
    138	return 0;
    139
    140err_fput:
    141	fput(file);
    142err_enckey:
    143	kfree_sensitive(enckey);
    144error:
    145	kvfree_sensitive(buf, enclen);
    146	return ret;
    147}
    148
    149/*
    150 * Clear preparsement.
    151 */
    152void big_key_free_preparse(struct key_preparsed_payload *prep)
    153{
    154	struct big_key_payload *payload = to_big_key_payload(prep->payload);
    155
    156	if (prep->datalen > BIG_KEY_FILE_THRESHOLD)
    157		path_put(&payload->path);
    158	kfree_sensitive(payload->data);
    159}
    160
    161/*
    162 * dispose of the links from a revoked keyring
    163 * - called with the key sem write-locked
    164 */
    165void big_key_revoke(struct key *key)
    166{
    167	struct big_key_payload *payload = to_big_key_payload(key->payload);
    168
    169	/* clear the quota */
    170	key_payload_reserve(key, 0);
    171	if (key_is_positive(key) && payload->length > BIG_KEY_FILE_THRESHOLD)
    172		vfs_truncate(&payload->path, 0);
    173}
    174
    175/*
    176 * dispose of the data dangling from the corpse of a big_key key
    177 */
    178void big_key_destroy(struct key *key)
    179{
    180	struct big_key_payload *payload = to_big_key_payload(key->payload);
    181
    182	if (payload->length > BIG_KEY_FILE_THRESHOLD) {
    183		path_put(&payload->path);
    184		payload->path.mnt = NULL;
    185		payload->path.dentry = NULL;
    186	}
    187	kfree_sensitive(payload->data);
    188	payload->data = NULL;
    189}
    190
    191/*
    192 * Update a big key
    193 */
    194int big_key_update(struct key *key, struct key_preparsed_payload *prep)
    195{
    196	int ret;
    197
    198	ret = key_payload_reserve(key, prep->datalen);
    199	if (ret < 0)
    200		return ret;
    201
    202	if (key_is_positive(key))
    203		big_key_destroy(key);
    204
    205	return generic_key_instantiate(key, prep);
    206}
    207
    208/*
    209 * describe the big_key key
    210 */
    211void big_key_describe(const struct key *key, struct seq_file *m)
    212{
    213	struct big_key_payload *payload = to_big_key_payload(key->payload);
    214
    215	seq_puts(m, key->description);
    216
    217	if (key_is_positive(key))
    218		seq_printf(m, ": %zu [%s]",
    219			   payload->length,
    220			   payload->length > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
    221}
    222
    223/*
    224 * read the key data
    225 * - the key's semaphore is read-locked
    226 */
    227long big_key_read(const struct key *key, char *buffer, size_t buflen)
    228{
    229	struct big_key_payload *payload = to_big_key_payload(key->payload);
    230	size_t datalen = payload->length;
    231	long ret;
    232
    233	if (!buffer || buflen < datalen)
    234		return datalen;
    235
    236	if (datalen > BIG_KEY_FILE_THRESHOLD) {
    237		struct file *file;
    238		u8 *buf, *enckey = payload->data;
    239		size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE;
    240		loff_t pos = 0;
    241
    242		buf = kvmalloc(enclen, GFP_KERNEL);
    243		if (!buf)
    244			return -ENOMEM;
    245
    246		file = dentry_open(&payload->path, O_RDONLY, current_cred());
    247		if (IS_ERR(file)) {
    248			ret = PTR_ERR(file);
    249			goto error;
    250		}
    251
    252		/* read file to kernel and decrypt */
    253		ret = kernel_read(file, buf, enclen, &pos);
    254		if (ret != enclen) {
    255			if (ret >= 0)
    256				ret = -EIO;
    257			goto err_fput;
    258		}
    259
    260		ret = chacha20poly1305_decrypt(buf, buf, enclen, NULL, 0, 0,
    261					       enckey) ? 0 : -EBADMSG;
    262		if (unlikely(ret))
    263			goto err_fput;
    264
    265		ret = datalen;
    266
    267		/* copy out decrypted data */
    268		memcpy(buffer, buf, datalen);
    269
    270err_fput:
    271		fput(file);
    272error:
    273		kvfree_sensitive(buf, enclen);
    274	} else {
    275		ret = datalen;
    276		memcpy(buffer, payload->data, datalen);
    277	}
    278
    279	return ret;
    280}
    281
    282/*
    283 * Register key type
    284 */
    285static int __init big_key_init(void)
    286{
    287	return register_key_type(&key_type_big_key);
    288}
    289
    290late_initcall(big_key_init);