diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
| commit | f4cdf7ca9a1fdcca413157df19753f388a5a224e (patch) | |
| tree | 60a31895b2446cd7fa27514e8c2843534a0de0da /security/keys/big_key.c | |
| download | linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip | |
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab:
- v4l2-core: added ISP statistics support and per-block validation
- v4l2-core: Allow unknown HDR10 white point and luminance
- New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor
- New codec: Milos: VPU v2.0 codec support
- isp driver: gained support for Dreamchip RPPX1 ISP framework
- vsp1 driver: gained support for RZ/T2H and RZ/N2H
- Novalake driver: gained CVS support for new NVL hardware
- dvb-core: fix feed leak on failed DMX_ADD_PID
- several driver fixes, cleanups and minor improvements
* tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
media: ipu-bridge: check all DMI entries when overriding sensor rotation
media: v4l2-async: avoid deleting unlinked ASC entry on link error
media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement
media: intel/ipu6: fix async notifier cleanup leak on parse error
media: staging/ipu7: fix async notifier UAF on probe error path
media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path
media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem()
media: rcar-isp: Fix VSPX reference leaks
media: rcar-isp: Release ISPCORE resources
media: i2c: imx415: Release runtime PM reference on VBLANK error
media: i2c: imx415: Return test pattern write errors
media: renesas: vsp1: Declare index variables in for loop statement
media: renesas: vsp1: Make reset control optional to support platforms without a reset line
media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs
media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs
media: nxp: imx8-isi: Add additional 32-bit RGB format support
media: nxp: imx8-isi: Add 16-bit raw Bayer format support
media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams
media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
media: nxp: imx8-isi: Correct color map between V4L2 and ISI
...
Diffstat (limited to 'security/keys/big_key.c')
| -rw-r--r-- | security/keys/big_key.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/security/keys/big_key.c b/security/keys/big_key.c new file mode 100644 index 000000000..268f702df --- /dev/null +++ b/security/keys/big_key.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Large capacity key type + * + * Copyright (C) 2017-2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "big_key: "fmt +#include <linux/init.h> +#include <linux/seq_file.h> +#include <linux/file.h> +#include <linux/shmem_fs.h> +#include <linux/err.h> +#include <linux/random.h> +#include <keys/user-type.h> +#include <keys/big_key-type.h> +#include <crypto/chacha20poly1305.h> + +/* + * Layout of key payload words. + */ +struct big_key_payload { + u8 *data; + struct path path; + size_t length; +}; +#define to_big_key_payload(payload) \ + (struct big_key_payload *)((payload).data) + +/* + * If the data is under this limit, there's no point creating a shm file to + * hold it as the permanently resident metadata for the shmem fs will be at + * least as large as the data. + */ +#define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry)) + +/* + * big_key defined keys take an arbitrary string as the description and an + * arbitrary blob of data as the payload + */ +struct key_type key_type_big_key = { + .name = "big_key", + .preparse = big_key_preparse, + .free_preparse = big_key_free_preparse, + .instantiate = generic_key_instantiate, + .revoke = big_key_revoke, + .destroy = big_key_destroy, + .describe = big_key_describe, + .read = big_key_read, + .update = big_key_update, +}; + +/* + * Preparse a big key + */ +int big_key_preparse(struct key_preparsed_payload *prep) +{ + struct big_key_payload *payload = to_big_key_payload(prep->payload); + struct file *file; + u8 *buf, *enckey; + ssize_t written; + size_t datalen = prep->datalen; + size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; + int ret; + + BUILD_BUG_ON(sizeof(*payload) != sizeof(prep->payload.data)); + + if (datalen == 0 || datalen > 1024 * 1024 || !prep->data) + return -EINVAL; + + /* Set an arbitrary quota */ + prep->quotalen = 16; + + payload->length = datalen; + + if (datalen > BIG_KEY_FILE_THRESHOLD) { + /* Create a shmem file to store the data in. This will permit the data + * to be swapped out if needed. + * + * File content is stored encrypted with randomly generated key. + * Since the key is random for each file, we can set the nonce + * to zero, provided we never define a ->update() call. + */ + loff_t pos = 0; + + buf = kvmalloc(enclen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* generate random key */ + enckey = kmalloc(CHACHA20POLY1305_KEY_SIZE, GFP_KERNEL); + if (!enckey) { + ret = -ENOMEM; + goto error; + } + ret = get_random_bytes_wait(enckey, CHACHA20POLY1305_KEY_SIZE); + if (unlikely(ret)) + goto err_enckey; + + /* encrypt data */ + chacha20poly1305_encrypt(buf, prep->data, datalen, NULL, 0, + 0, enckey); + + /* save aligned data to file */ + file = shmem_kernel_file_setup("", enclen, EMPTY_VMA_FLAGS); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto err_enckey; + } + + written = kernel_write(file, buf, enclen, &pos); + if (written != enclen) { + ret = written; + if (written >= 0) + ret = -EIO; + goto err_fput; + } + + /* Pin the mount and dentry to the key so that we can open it again + * later + */ + payload->data = enckey; + payload->path = file->f_path; + path_get(&payload->path); + fput(file); + kvfree_sensitive(buf, enclen); + } else { + /* Just store the data in a buffer */ + void *data = kmalloc(datalen, GFP_KERNEL); + + if (!data) + return -ENOMEM; + + payload->data = data; + memcpy(data, prep->data, prep->datalen); + } + return 0; + +err_fput: + fput(file); +err_enckey: + kfree_sensitive(enckey); +error: + kvfree_sensitive(buf, enclen); + return ret; +} + +/* + * Clear preparsement. + */ +void big_key_free_preparse(struct key_preparsed_payload *prep) +{ + struct big_key_payload *payload = to_big_key_payload(prep->payload); + + if (prep->datalen > BIG_KEY_FILE_THRESHOLD) + path_put(&payload->path); + kfree_sensitive(payload->data); +} + +/* + * dispose of the links from a revoked keyring + * - called with the key sem write-locked + */ +void big_key_revoke(struct key *key) +{ + struct big_key_payload *payload = to_big_key_payload(key->payload); + + /* clear the quota */ + key_payload_reserve(key, 0); + if (key_is_positive(key) && payload->length > BIG_KEY_FILE_THRESHOLD) + vfs_truncate(&payload->path, 0); +} + +/* + * dispose of the data dangling from the corpse of a big_key key + */ +void big_key_destroy(struct key *key) +{ + struct big_key_payload *payload = to_big_key_payload(key->payload); + + if (payload->length > BIG_KEY_FILE_THRESHOLD) { + path_put(&payload->path); + payload->path.mnt = NULL; + payload->path.dentry = NULL; + } + kfree_sensitive(payload->data); + payload->data = NULL; +} + +/* + * Update a big key + */ +int big_key_update(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + ret = key_payload_reserve(key, prep->datalen); + if (ret < 0) + return ret; + + if (key_is_positive(key)) + big_key_destroy(key); + + return generic_key_instantiate(key, prep); +} + +/* + * describe the big_key key + */ +void big_key_describe(const struct key *key, struct seq_file *m) +{ + struct big_key_payload *payload = to_big_key_payload(key->payload); + + seq_puts(m, key->description); + + if (key_is_positive(key)) + seq_printf(m, ": %zu [%s]", + payload->length, + payload->length > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); +} + +/* + * read the key data + * - the key's semaphore is read-locked + */ +long big_key_read(const struct key *key, char *buffer, size_t buflen) +{ + struct big_key_payload *payload = to_big_key_payload(key->payload); + size_t datalen = payload->length; + long ret; + + if (!buffer || buflen < datalen) + return datalen; + + if (datalen > BIG_KEY_FILE_THRESHOLD) { + struct file *file; + u8 *buf, *enckey = payload->data; + size_t enclen = datalen + CHACHA20POLY1305_AUTHTAG_SIZE; + loff_t pos = 0; + + buf = kvmalloc(enclen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + file = dentry_open(&payload->path, O_RDONLY, current_cred()); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto error; + } + + /* read file to kernel and decrypt */ + ret = kernel_read(file, buf, enclen, &pos); + if (ret != enclen) { + if (ret >= 0) + ret = -EIO; + goto err_fput; + } + + ret = chacha20poly1305_decrypt(buf, buf, enclen, NULL, 0, 0, + enckey) ? 0 : -EBADMSG; + if (unlikely(ret)) + goto err_fput; + + ret = datalen; + + /* copy out decrypted data */ + memcpy(buffer, buf, datalen); + +err_fput: + fput(file); +error: + kvfree_sensitive(buf, enclen); + } else { + ret = datalen; + memcpy(buffer, payload->data, datalen); + } + + return ret; +} + +/* + * Register key type + */ +static int __init big_key_init(void) +{ + return register_key_type(&key_type_big_key); +} + +late_initcall(big_key_init); |
