Blame view
security/keys/big_key.c
10.1 KB
b4d0d230c treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
ab3c3587f KEYS: Implement a... |
2 3 |
/* Large capacity key type * |
428490e38 security/keys: re... |
4 |
* Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. |
ab3c3587f KEYS: Implement a... |
5 6 |
* Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) |
ab3c3587f KEYS: Implement a... |
7 |
*/ |
7df3e59c3 KEYS: Sort out bi... |
8 |
#define pr_fmt(fmt) "big_key: "fmt |
ab3c3587f KEYS: Implement a... |
9 10 11 12 13 |
#include <linux/init.h> #include <linux/seq_file.h> #include <linux/file.h> #include <linux/shmem_fs.h> #include <linux/err.h> |
13100a72f Security: Keys: B... |
14 |
#include <linux/scatterlist.h> |
428490e38 security/keys: re... |
15 |
#include <linux/random.h> |
514c60324 headers: untangle... |
16 |
#include <linux/vmalloc.h> |
ab3c3587f KEYS: Implement a... |
17 18 |
#include <keys/user-type.h> #include <keys/big_key-type.h> |
428490e38 security/keys: re... |
19 |
#include <crypto/aead.h> |
a964f3956 big key: get rid ... |
20 |
#include <crypto/gcm.h> |
ab3c3587f KEYS: Implement a... |
21 |
|
d9f4bb1a0 KEYS: Use individ... |
22 23 24 25 26 27 |
struct big_key_buf { unsigned int nr_pages; void *virt; struct scatterlist *sg; struct page *pages[]; }; |
ab3c3587f KEYS: Implement a... |
28 |
/* |
146aa8b14 KEYS: Merge the t... |
29 30 31 32 33 34 35 36 37 38 |
* Layout of key payload words. */ enum { big_key_data, big_key_path, big_key_path_2nd_part, big_key_len, }; /* |
13100a72f Security: Keys: B... |
39 40 41 42 43 44 45 46 |
* Crypto operation with big_key data */ enum big_key_op { BIG_KEY_ENC, BIG_KEY_DEC, }; /* |
ab3c3587f KEYS: Implement a... |
47 48 49 50 51 52 53 |
* 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)) /* |
13100a72f Security: Keys: B... |
54 55 |
* Key size for big_key data encryption */ |
428490e38 security/keys: re... |
56 57 58 59 60 61 |
#define ENC_KEY_SIZE 32 /* * Authentication tag length */ #define ENC_AUTHTAG_SIZE 16 |
13100a72f Security: Keys: B... |
62 63 |
/* |
ab3c3587f KEYS: Implement a... |
64 65 66 67 68 |
* 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", |
002edaf76 KEYS: big_key: Us... |
69 70 71 |
.preparse = big_key_preparse, .free_preparse = big_key_free_preparse, .instantiate = generic_key_instantiate, |
ab3c3587f KEYS: Implement a... |
72 73 74 75 |
.revoke = big_key_revoke, .destroy = big_key_destroy, .describe = big_key_describe, .read = big_key_read, |
428490e38 security/keys: re... |
76 |
/* no ->update(); don't add it without changing big_key_crypt() nonce */ |
ab3c3587f KEYS: Implement a... |
77 78 79 |
}; /* |
428490e38 security/keys: re... |
80 |
* Crypto names for big_key data authenticated encryption |
13100a72f Security: Keys: B... |
81 |
*/ |
428490e38 security/keys: re... |
82 |
static const char big_key_alg_name[] = "gcm(aes)"; |
a964f3956 big key: get rid ... |
83 |
#define BIG_KEY_IV_SIZE GCM_AES_IV_SIZE |
13100a72f Security: Keys: B... |
84 85 |
/* |
428490e38 security/keys: re... |
86 |
* Crypto algorithms for big_key data authenticated encryption |
13100a72f Security: Keys: B... |
87 |
*/ |
428490e38 security/keys: re... |
88 |
static struct crypto_aead *big_key_aead; |
13100a72f Security: Keys: B... |
89 90 |
/* |
428490e38 security/keys: re... |
91 |
* Since changing the key affects the entire object, we need a mutex. |
13100a72f Security: Keys: B... |
92 |
*/ |
428490e38 security/keys: re... |
93 |
static DEFINE_MUTEX(big_key_aead_lock); |
13100a72f Security: Keys: B... |
94 95 96 97 |
/* * Encrypt/decrypt big_key data */ |
d9f4bb1a0 KEYS: Use individ... |
98 |
static int big_key_crypt(enum big_key_op op, struct big_key_buf *buf, size_t datalen, u8 *key) |
13100a72f Security: Keys: B... |
99 |
{ |
428490e38 security/keys: re... |
100 |
int ret; |
428490e38 security/keys: re... |
101 102 103 104 105 106 107 |
struct aead_request *aead_req; /* We always use a zero nonce. The reason we can get away with this is * because we're using a different randomly generated key for every * different encryption. Notably, too, key_type_big_key doesn't define * an .update function, so there's no chance we'll wind up reusing the * key to encrypt updated data. Simply put: one key, one encryption. */ |
a964f3956 big key: get rid ... |
108 |
u8 zero_nonce[BIG_KEY_IV_SIZE]; |
428490e38 security/keys: re... |
109 110 111 112 113 114 |
aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL); if (!aead_req) return -ENOMEM; memset(zero_nonce, 0, sizeof(zero_nonce)); |
d9f4bb1a0 KEYS: Use individ... |
115 |
aead_request_set_crypt(aead_req, buf->sg, buf->sg, datalen, zero_nonce); |
428490e38 security/keys: re... |
116 117 118 119 120 |
aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); aead_request_set_ad(aead_req, 0); mutex_lock(&big_key_aead_lock); if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) { |
13100a72f Security: Keys: B... |
121 122 123 |
ret = -EAGAIN; goto error; } |
13100a72f Security: Keys: B... |
124 |
if (op == BIG_KEY_ENC) |
428490e38 security/keys: re... |
125 |
ret = crypto_aead_encrypt(aead_req); |
13100a72f Security: Keys: B... |
126 |
else |
428490e38 security/keys: re... |
127 |
ret = crypto_aead_decrypt(aead_req); |
13100a72f Security: Keys: B... |
128 |
error: |
428490e38 security/keys: re... |
129 130 |
mutex_unlock(&big_key_aead_lock); aead_request_free(aead_req); |
13100a72f Security: Keys: B... |
131 132 133 134 |
return ret; } /* |
d9f4bb1a0 KEYS: Use individ... |
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
* Free up the buffer. */ static void big_key_free_buffer(struct big_key_buf *buf) { unsigned int i; if (buf->virt) { memset(buf->virt, 0, buf->nr_pages * PAGE_SIZE); vunmap(buf->virt); } for (i = 0; i < buf->nr_pages; i++) if (buf->pages[i]) __free_page(buf->pages[i]); kfree(buf); } /* * Allocate a buffer consisting of a set of pages with a virtual mapping * applied over them. */ static void *big_key_alloc_buffer(size_t len) { struct big_key_buf *buf; unsigned int npg = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; unsigned int i, l; buf = kzalloc(sizeof(struct big_key_buf) + sizeof(struct page) * npg + sizeof(struct scatterlist) * npg, GFP_KERNEL); if (!buf) return NULL; buf->nr_pages = npg; buf->sg = (void *)(buf->pages + npg); sg_init_table(buf->sg, npg); for (i = 0; i < buf->nr_pages; i++) { buf->pages[i] = alloc_page(GFP_KERNEL); if (!buf->pages[i]) goto nomem; l = min_t(size_t, len, PAGE_SIZE); sg_set_page(&buf->sg[i], buf->pages[i], l, 0); len -= l; } buf->virt = vmap(buf->pages, buf->nr_pages, VM_MAP, PAGE_KERNEL); if (!buf->virt) goto nomem; return buf; nomem: big_key_free_buffer(buf); return NULL; } /* |
002edaf76 KEYS: big_key: Us... |
196 |
* Preparse a big key |
ab3c3587f KEYS: Implement a... |
197 |
*/ |
002edaf76 KEYS: big_key: Us... |
198 |
int big_key_preparse(struct key_preparsed_payload *prep) |
ab3c3587f KEYS: Implement a... |
199 |
{ |
d9f4bb1a0 KEYS: Use individ... |
200 |
struct big_key_buf *buf; |
146aa8b14 KEYS: Merge the t... |
201 |
struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
ab3c3587f KEYS: Implement a... |
202 |
struct file *file; |
13100a72f Security: Keys: B... |
203 |
u8 *enckey; |
ab3c3587f KEYS: Implement a... |
204 |
ssize_t written; |
d9f4bb1a0 KEYS: Use individ... |
205 |
size_t datalen = prep->datalen, enclen = datalen + ENC_AUTHTAG_SIZE; |
ab3c3587f KEYS: Implement a... |
206 |
int ret; |
ab3c3587f KEYS: Implement a... |
207 |
if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) |
d9f4bb1a0 KEYS: Use individ... |
208 |
return -EINVAL; |
ab3c3587f KEYS: Implement a... |
209 210 |
/* Set an arbitrary quota */ |
002edaf76 KEYS: big_key: Us... |
211 |
prep->quotalen = 16; |
ab3c3587f KEYS: Implement a... |
212 |
|
146aa8b14 KEYS: Merge the t... |
213 |
prep->payload.data[big_key_len] = (void *)(unsigned long)datalen; |
ab3c3587f KEYS: Implement a... |
214 215 216 217 218 |
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. * |
13100a72f Security: Keys: B... |
219 |
* File content is stored encrypted with randomly generated key. |
ab3c3587f KEYS: Implement a... |
220 |
*/ |
e13ec939e fs: fix kernel_wr... |
221 |
loff_t pos = 0; |
13100a72f Security: Keys: B... |
222 |
|
d9f4bb1a0 KEYS: Use individ... |
223 224 |
buf = big_key_alloc_buffer(enclen); if (!buf) |
13100a72f Security: Keys: B... |
225 |
return -ENOMEM; |
d9f4bb1a0 KEYS: Use individ... |
226 |
memcpy(buf->virt, prep->data, datalen); |
13100a72f Security: Keys: B... |
227 228 229 230 231 232 233 |
/* generate random key */ enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); if (!enckey) { ret = -ENOMEM; goto error; } |
428490e38 security/keys: re... |
234 235 |
ret = get_random_bytes_wait(enckey, ENC_KEY_SIZE); if (unlikely(ret)) |
13100a72f Security: Keys: B... |
236 237 238 |
goto err_enckey; /* encrypt aligned data */ |
d9f4bb1a0 KEYS: Use individ... |
239 |
ret = big_key_crypt(BIG_KEY_ENC, buf, datalen, enckey); |
13100a72f Security: Keys: B... |
240 241 242 243 244 |
if (ret) goto err_enckey; /* save aligned data to file */ file = shmem_kernel_file_setup("", enclen, 0); |
d2b869702 KEYS: fix error r... |
245 246 |
if (IS_ERR(file)) { ret = PTR_ERR(file); |
13100a72f Security: Keys: B... |
247 |
goto err_enckey; |
d2b869702 KEYS: fix error r... |
248 |
} |
ab3c3587f KEYS: Implement a... |
249 |
|
d9f4bb1a0 KEYS: Use individ... |
250 |
written = kernel_write(file, buf->virt, enclen, &pos); |
13100a72f Security: Keys: B... |
251 |
if (written != enclen) { |
97826c821 KEYS: Fix error h... |
252 |
ret = written; |
ab3c3587f KEYS: Implement a... |
253 254 255 256 257 258 259 260 |
if (written >= 0) ret = -ENOMEM; goto err_fput; } /* Pin the mount and dentry to the key so that we can open it again * later */ |
13100a72f Security: Keys: B... |
261 |
prep->payload.data[big_key_data] = enckey; |
ab3c3587f KEYS: Implement a... |
262 263 264 |
*path = file->f_path; path_get(path); fput(file); |
d9f4bb1a0 KEYS: Use individ... |
265 |
big_key_free_buffer(buf); |
ab3c3587f KEYS: Implement a... |
266 267 268 |
} else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); |
13100a72f Security: Keys: B... |
269 |
|
002edaf76 KEYS: big_key: Us... |
270 271 |
if (!data) return -ENOMEM; |
ab3c3587f KEYS: Implement a... |
272 |
|
146aa8b14 KEYS: Merge the t... |
273 274 |
prep->payload.data[big_key_data] = data; memcpy(data, prep->data, prep->datalen); |
ab3c3587f KEYS: Implement a... |
275 276 277 278 279 |
} return 0; err_fput: fput(file); |
13100a72f Security: Keys: B... |
280 |
err_enckey: |
910801809 security/keys: pr... |
281 |
kzfree(enckey); |
ab3c3587f KEYS: Implement a... |
282 |
error: |
d9f4bb1a0 KEYS: Use individ... |
283 |
big_key_free_buffer(buf); |
ab3c3587f KEYS: Implement a... |
284 285 286 287 |
return ret; } /* |
002edaf76 KEYS: big_key: Us... |
288 289 290 291 292 |
* Clear preparsement. */ void big_key_free_preparse(struct key_preparsed_payload *prep) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { |
146aa8b14 KEYS: Merge the t... |
293 |
struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
13100a72f Security: Keys: B... |
294 |
|
002edaf76 KEYS: big_key: Us... |
295 |
path_put(path); |
002edaf76 KEYS: big_key: Us... |
296 |
} |
910801809 security/keys: pr... |
297 |
kzfree(prep->payload.data[big_key_data]); |
002edaf76 KEYS: big_key: Us... |
298 299 300 |
} /* |
ab3c3587f KEYS: Implement a... |
301 302 303 304 305 |
* dispose of the links from a revoked keyring * - called with the key sem write-locked */ void big_key_revoke(struct key *key) { |
146aa8b14 KEYS: Merge the t... |
306 |
struct path *path = (struct path *)&key->payload.data[big_key_path]; |
ab3c3587f KEYS: Implement a... |
307 308 309 |
/* clear the quota */ key_payload_reserve(key, 0); |
363b02dab KEYS: Fix race be... |
310 |
if (key_is_positive(key) && |
146aa8b14 KEYS: Merge the t... |
311 |
(size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) |
ab3c3587f KEYS: Implement a... |
312 313 314 315 316 317 318 319 |
vfs_truncate(path, 0); } /* * dispose of the data dangling from the corpse of a big_key key */ void big_key_destroy(struct key *key) { |
146aa8b14 KEYS: Merge the t... |
320 |
size_t datalen = (size_t)key->payload.data[big_key_len]; |
13100a72f Security: Keys: B... |
321 |
if (datalen > BIG_KEY_FILE_THRESHOLD) { |
146aa8b14 KEYS: Merge the t... |
322 |
struct path *path = (struct path *)&key->payload.data[big_key_path]; |
13100a72f Security: Keys: B... |
323 |
|
ab3c3587f KEYS: Implement a... |
324 325 326 |
path_put(path); path->mnt = NULL; path->dentry = NULL; |
ab3c3587f KEYS: Implement a... |
327 |
} |
910801809 security/keys: pr... |
328 |
kzfree(key->payload.data[big_key_data]); |
13100a72f Security: Keys: B... |
329 |
key->payload.data[big_key_data] = NULL; |
ab3c3587f KEYS: Implement a... |
330 331 332 333 334 335 336 |
} /* * describe the big_key key */ void big_key_describe(const struct key *key, struct seq_file *m) { |
146aa8b14 KEYS: Merge the t... |
337 |
size_t datalen = (size_t)key->payload.data[big_key_len]; |
ab3c3587f KEYS: Implement a... |
338 339 |
seq_puts(m, key->description); |
363b02dab KEYS: Fix race be... |
340 |
if (key_is_positive(key)) |
146aa8b14 KEYS: Merge the t... |
341 |
seq_printf(m, ": %zu [%s]", |
ab3c3587f KEYS: Implement a... |
342 343 344 345 346 347 348 349 350 351 |
datalen, datalen > 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 __user *buffer, size_t buflen) { |
146aa8b14 KEYS: Merge the t... |
352 |
size_t datalen = (size_t)key->payload.data[big_key_len]; |
ab3c3587f KEYS: Implement a... |
353 354 355 356 357 358 |
long ret; if (!buffer || buflen < datalen) return datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { |
d9f4bb1a0 KEYS: Use individ... |
359 |
struct big_key_buf *buf; |
146aa8b14 KEYS: Merge the t... |
360 |
struct path *path = (struct path *)&key->payload.data[big_key_path]; |
ab3c3587f KEYS: Implement a... |
361 |
struct file *file; |
13100a72f Security: Keys: B... |
362 |
u8 *enckey = (u8 *)key->payload.data[big_key_data]; |
428490e38 security/keys: re... |
363 |
size_t enclen = datalen + ENC_AUTHTAG_SIZE; |
bdd1d2d3d fs: fix kernel_re... |
364 |
loff_t pos = 0; |
13100a72f Security: Keys: B... |
365 |
|
d9f4bb1a0 KEYS: Use individ... |
366 367 |
buf = big_key_alloc_buffer(enclen); if (!buf) |
13100a72f Security: Keys: B... |
368 |
return -ENOMEM; |
ab3c3587f KEYS: Implement a... |
369 370 |
file = dentry_open(path, O_RDONLY, current_cred()); |
13100a72f Security: Keys: B... |
371 372 373 374 |
if (IS_ERR(file)) { ret = PTR_ERR(file); goto error; } |
ab3c3587f KEYS: Implement a... |
375 |
|
13100a72f Security: Keys: B... |
376 |
/* read file to kernel and decrypt */ |
d9f4bb1a0 KEYS: Use individ... |
377 |
ret = kernel_read(file, buf->virt, enclen, &pos); |
13100a72f Security: Keys: B... |
378 |
if (ret >= 0 && ret != enclen) { |
ab3c3587f KEYS: Implement a... |
379 |
ret = -EIO; |
13100a72f Security: Keys: B... |
380 381 |
goto err_fput; } |
d9f4bb1a0 KEYS: Use individ... |
382 |
ret = big_key_crypt(BIG_KEY_DEC, buf, enclen, enckey); |
13100a72f Security: Keys: B... |
383 384 385 386 387 388 |
if (ret) goto err_fput; ret = datalen; /* copy decrypted data to user */ |
d9f4bb1a0 KEYS: Use individ... |
389 |
if (copy_to_user(buffer, buf->virt, datalen) != 0) |
13100a72f Security: Keys: B... |
390 391 392 393 394 |
ret = -EFAULT; err_fput: fput(file); error: |
d9f4bb1a0 KEYS: Use individ... |
395 |
big_key_free_buffer(buf); |
ab3c3587f KEYS: Implement a... |
396 397 |
} else { ret = datalen; |
146aa8b14 KEYS: Merge the t... |
398 399 |
if (copy_to_user(buffer, key->payload.data[big_key_data], datalen) != 0) |
ab3c3587f KEYS: Implement a... |
400 401 402 403 404 |
ret = -EFAULT; } return ret; } |
13100a72f Security: Keys: B... |
405 406 407 |
/* * Register key type */ |
ab3c3587f KEYS: Implement a... |
408 409 |
static int __init big_key_init(void) { |
7df3e59c3 KEYS: Sort out bi... |
410 |
int ret; |
13100a72f Security: Keys: B... |
411 |
|
13100a72f Security: Keys: B... |
412 |
/* init block cipher */ |
428490e38 security/keys: re... |
413 414 415 |
big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(big_key_aead)) { ret = PTR_ERR(big_key_aead); |
7df3e59c3 KEYS: Sort out bi... |
416 417 |
pr_err("Can't alloc crypto: %d ", ret); |
428490e38 security/keys: re... |
418 419 |
return ret; } |
a964f3956 big key: get rid ... |
420 421 422 423 424 425 |
if (unlikely(crypto_aead_ivsize(big_key_aead) != BIG_KEY_IV_SIZE)) { WARN(1, "big key algorithm changed?"); ret = -EINVAL; goto free_aead; } |
428490e38 security/keys: re... |
426 427 428 429 430 |
ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE); if (ret < 0) { pr_err("Can't set crypto auth tag len: %d ", ret); goto free_aead; |
7df3e59c3 KEYS: Sort out bi... |
431 |
} |
7df3e59c3 KEYS: Sort out bi... |
432 433 434 435 436 |
ret = register_key_type(&key_type_big_key); if (ret < 0) { pr_err("Can't register type: %d ", ret); |
428490e38 security/keys: re... |
437 |
goto free_aead; |
13100a72f Security: Keys: B... |
438 439 440 |
} return 0; |
428490e38 security/keys: re... |
441 442 |
free_aead: crypto_free_aead(big_key_aead); |
13100a72f Security: Keys: B... |
443 444 |
return ret; } |
7df3e59c3 KEYS: Sort out bi... |
445 |
late_initcall(big_key_init); |