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