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 */ 23 struct 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 */ 42 struct 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 */ 57 int 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 140 err_fput: 141 fput(file); 142 err_enckey: 143 kfree_sensitive(enckey); 144 error: 145 kvfree_sensitive(buf, enclen); 146 return ret; 147 } 148 149 /* 150 * Clear preparsement. 151 */ 152 void 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 */ 165 void 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 */ 178 void 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 */ 194 int 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 */ 211 void 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 */ 227 long 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 270 err_fput: 271 fput(file); 272 error: 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 */ 285 static int __init big_key_init(void) 286 { 287 return register_key_type(&key_type_big_key); 288 } 289 290 late_initcall(big_key_init); 291
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.