Blame view
security/keys/big_key.c
8.5 KB
ab3c3587f
|
1 2 3 4 5 6 7 8 9 10 |
/* Large capacity key type * * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public Licence * as published by the Free Software Foundation; either version * 2 of the Licence, or (at your option) any later version. */ |
7df3e59c3
|
11 |
#define pr_fmt(fmt) "big_key: "fmt |
ab3c3587f
|
12 13 14 15 16 |
#include <linux/init.h> #include <linux/seq_file.h> #include <linux/file.h> #include <linux/shmem_fs.h> #include <linux/err.h> |
13100a72f
|
17 |
#include <linux/scatterlist.h> |
ab3c3587f
|
18 19 |
#include <keys/user-type.h> #include <keys/big_key-type.h> |
13100a72f
|
20 |
#include <crypto/rng.h> |
d56d72c6a
|
21 |
#include <crypto/skcipher.h> |
ab3c3587f
|
22 |
|
ab3c3587f
|
23 |
/* |
146aa8b14
|
24 25 26 27 28 29 30 31 32 33 |
* Layout of key payload words. */ enum { big_key_data, big_key_path, big_key_path_2nd_part, big_key_len, }; /* |
13100a72f
|
34 35 36 37 38 39 40 41 |
* Crypto operation with big_key data */ enum big_key_op { BIG_KEY_ENC, BIG_KEY_DEC, }; /* |
ab3c3587f
|
42 43 44 45 46 47 48 |
* 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
|
49 50 51 52 53 |
* Key size for big_key data encryption */ #define ENC_KEY_SIZE 16 /* |
ab3c3587f
|
54 55 56 57 58 |
* 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
|
59 60 61 |
.preparse = big_key_preparse, .free_preparse = big_key_free_preparse, .instantiate = generic_key_instantiate, |
ab3c3587f
|
62 63 64 65 66 67 68 |
.revoke = big_key_revoke, .destroy = big_key_destroy, .describe = big_key_describe, .read = big_key_read, }; /* |
13100a72f
|
69 70 71 72 73 74 75 76 77 |
* Crypto names for big_key data encryption */ static const char big_key_rng_name[] = "stdrng"; static const char big_key_alg_name[] = "ecb(aes)"; /* * Crypto algorithms for big_key data encryption */ static struct crypto_rng *big_key_rng; |
d56d72c6a
|
78 |
static struct crypto_skcipher *big_key_skcipher; |
13100a72f
|
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
/* * Generate random key to encrypt big_key data */ static inline int big_key_gen_enckey(u8 *key) { return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE); } /* * Encrypt/decrypt big_key data */ static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key) { int ret = -EINVAL; struct scatterlist sgio; |
d56d72c6a
|
95 |
SKCIPHER_REQUEST_ON_STACK(req, big_key_skcipher); |
13100a72f
|
96 |
|
d56d72c6a
|
97 |
if (crypto_skcipher_setkey(big_key_skcipher, key, ENC_KEY_SIZE)) { |
13100a72f
|
98 99 100 |
ret = -EAGAIN; goto error; } |
d56d72c6a
|
101 102 103 |
skcipher_request_set_tfm(req, big_key_skcipher); skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL); |
13100a72f
|
104 105 |
sg_init_one(&sgio, data, datalen); |
d56d72c6a
|
106 |
skcipher_request_set_crypt(req, &sgio, &sgio, datalen, NULL); |
13100a72f
|
107 108 |
if (op == BIG_KEY_ENC) |
d56d72c6a
|
109 |
ret = crypto_skcipher_encrypt(req); |
13100a72f
|
110 |
else |
d56d72c6a
|
111 112 113 |
ret = crypto_skcipher_decrypt(req); skcipher_request_zero(req); |
13100a72f
|
114 115 116 117 118 119 |
error: return ret; } /* |
002edaf76
|
120 |
* Preparse a big key |
ab3c3587f
|
121 |
*/ |
002edaf76
|
122 |
int big_key_preparse(struct key_preparsed_payload *prep) |
ab3c3587f
|
123 |
{ |
146aa8b14
|
124 |
struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
ab3c3587f
|
125 |
struct file *file; |
13100a72f
|
126 127 |
u8 *enckey; u8 *data = NULL; |
ab3c3587f
|
128 129 130 131 132 133 134 135 136 |
ssize_t written; size_t datalen = prep->datalen; int ret; ret = -EINVAL; if (datalen <= 0 || datalen > 1024 * 1024 || !prep->data) goto error; /* Set an arbitrary quota */ |
002edaf76
|
137 |
prep->quotalen = 16; |
ab3c3587f
|
138 |
|
146aa8b14
|
139 |
prep->payload.data[big_key_len] = (void *)(unsigned long)datalen; |
ab3c3587f
|
140 141 142 143 144 |
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
|
145 |
* File content is stored encrypted with randomly generated key. |
ab3c3587f
|
146 |
*/ |
d56d72c6a
|
147 |
size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher)); |
13100a72f
|
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 |
/* prepare aligned data to encrypt */ data = kmalloc(enclen, GFP_KERNEL); if (!data) return -ENOMEM; memcpy(data, prep->data, datalen); memset(data + datalen, 0x00, enclen - datalen); /* generate random key */ enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL); if (!enckey) { ret = -ENOMEM; goto error; } ret = big_key_gen_enckey(enckey); if (ret) goto err_enckey; /* encrypt aligned data */ ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey); if (ret) goto err_enckey; /* save aligned data to file */ file = shmem_kernel_file_setup("", enclen, 0); |
d2b869702
|
175 176 |
if (IS_ERR(file)) { ret = PTR_ERR(file); |
13100a72f
|
177 |
goto err_enckey; |
d2b869702
|
178 |
} |
ab3c3587f
|
179 |
|
13100a72f
|
180 181 |
written = kernel_write(file, data, enclen, 0); if (written != enclen) { |
97826c821
|
182 |
ret = written; |
ab3c3587f
|
183 184 185 186 187 188 189 190 |
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
|
191 |
prep->payload.data[big_key_data] = enckey; |
ab3c3587f
|
192 193 194 |
*path = file->f_path; path_get(path); fput(file); |
13100a72f
|
195 |
kfree(data); |
ab3c3587f
|
196 197 198 |
} else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); |
13100a72f
|
199 |
|
002edaf76
|
200 201 |
if (!data) return -ENOMEM; |
ab3c3587f
|
202 |
|
146aa8b14
|
203 204 |
prep->payload.data[big_key_data] = data; memcpy(data, prep->data, prep->datalen); |
ab3c3587f
|
205 206 207 208 209 |
} return 0; err_fput: fput(file); |
13100a72f
|
210 211 |
err_enckey: kfree(enckey); |
ab3c3587f
|
212 |
error: |
13100a72f
|
213 |
kfree(data); |
ab3c3587f
|
214 215 216 217 |
return ret; } /* |
002edaf76
|
218 219 220 221 222 |
* Clear preparsement. */ void big_key_free_preparse(struct key_preparsed_payload *prep) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { |
146aa8b14
|
223 |
struct path *path = (struct path *)&prep->payload.data[big_key_path]; |
13100a72f
|
224 |
|
002edaf76
|
225 |
path_put(path); |
002edaf76
|
226 |
} |
13100a72f
|
227 |
kfree(prep->payload.data[big_key_data]); |
002edaf76
|
228 229 230 |
} /* |
ab3c3587f
|
231 232 233 234 235 |
* dispose of the links from a revoked keyring * - called with the key sem write-locked */ void big_key_revoke(struct key *key) { |
146aa8b14
|
236 |
struct path *path = (struct path *)&key->payload.data[big_key_path]; |
ab3c3587f
|
237 238 239 |
/* clear the quota */ key_payload_reserve(key, 0); |
146aa8b14
|
240 241 |
if (key_is_instantiated(key) && (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) |
ab3c3587f
|
242 243 244 245 246 247 248 249 |
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
|
250 |
size_t datalen = (size_t)key->payload.data[big_key_len]; |
13100a72f
|
251 |
if (datalen > BIG_KEY_FILE_THRESHOLD) { |
146aa8b14
|
252 |
struct path *path = (struct path *)&key->payload.data[big_key_path]; |
13100a72f
|
253 |
|
ab3c3587f
|
254 255 256 |
path_put(path); path->mnt = NULL; path->dentry = NULL; |
ab3c3587f
|
257 |
} |
13100a72f
|
258 259 |
kfree(key->payload.data[big_key_data]); key->payload.data[big_key_data] = NULL; |
ab3c3587f
|
260 261 262 263 264 265 266 |
} /* * describe the big_key key */ void big_key_describe(const struct key *key, struct seq_file *m) { |
146aa8b14
|
267 |
size_t datalen = (size_t)key->payload.data[big_key_len]; |
ab3c3587f
|
268 269 270 271 |
seq_puts(m, key->description); if (key_is_instantiated(key)) |
146aa8b14
|
272 |
seq_printf(m, ": %zu [%s]", |
ab3c3587f
|
273 274 275 276 277 278 279 280 281 282 |
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
|
283 |
size_t datalen = (size_t)key->payload.data[big_key_len]; |
ab3c3587f
|
284 285 286 287 288 289 |
long ret; if (!buffer || buflen < datalen) return datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { |
146aa8b14
|
290 |
struct path *path = (struct path *)&key->payload.data[big_key_path]; |
ab3c3587f
|
291 |
struct file *file; |
13100a72f
|
292 293 |
u8 *data; u8 *enckey = (u8 *)key->payload.data[big_key_data]; |
d56d72c6a
|
294 |
size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher)); |
13100a72f
|
295 296 297 298 |
data = kmalloc(enclen, GFP_KERNEL); if (!data) return -ENOMEM; |
ab3c3587f
|
299 300 |
file = dentry_open(path, O_RDONLY, current_cred()); |
13100a72f
|
301 302 303 304 |
if (IS_ERR(file)) { ret = PTR_ERR(file); goto error; } |
ab3c3587f
|
305 |
|
13100a72f
|
306 307 308 |
/* read file to kernel and decrypt */ ret = kernel_read(file, 0, data, enclen); if (ret >= 0 && ret != enclen) { |
ab3c3587f
|
309 |
ret = -EIO; |
13100a72f
|
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
goto err_fput; } ret = big_key_crypt(BIG_KEY_DEC, data, enclen, enckey); if (ret) goto err_fput; ret = datalen; /* copy decrypted data to user */ if (copy_to_user(buffer, data, datalen) != 0) ret = -EFAULT; err_fput: fput(file); error: kfree(data); |
ab3c3587f
|
327 328 |
} else { ret = datalen; |
146aa8b14
|
329 330 |
if (copy_to_user(buffer, key->payload.data[big_key_data], datalen) != 0) |
ab3c3587f
|
331 332 333 334 335 |
ret = -EFAULT; } return ret; } |
13100a72f
|
336 337 338 |
/* * Register key type */ |
ab3c3587f
|
339 340 |
static int __init big_key_init(void) { |
7df3e59c3
|
341 342 343 |
struct crypto_skcipher *cipher; struct crypto_rng *rng; int ret; |
13100a72f
|
344 |
|
7df3e59c3
|
345 346 347 348 349 |
rng = crypto_alloc_rng(big_key_rng_name, 0, 0); if (IS_ERR(rng)) { pr_err("Can't alloc rng: %ld ", PTR_ERR(rng)); return PTR_ERR(rng); |
13100a72f
|
350 |
} |
7df3e59c3
|
351 |
big_key_rng = rng; |
13100a72f
|
352 |
/* seed RNG */ |
7df3e59c3
|
353 354 355 356 357 358 |
ret = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng)); if (ret) { pr_err("Can't reset rng: %d ", ret); goto error_rng; } |
13100a72f
|
359 360 |
/* init block cipher */ |
7df3e59c3
|
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
cipher = crypto_alloc_skcipher(big_key_alg_name, 0, CRYPTO_ALG_ASYNC); if (IS_ERR(cipher)) { ret = PTR_ERR(cipher); pr_err("Can't alloc crypto: %d ", ret); goto error_rng; } big_key_skcipher = cipher; ret = register_key_type(&key_type_big_key); if (ret < 0) { pr_err("Can't register type: %d ", ret); goto error_cipher; |
13100a72f
|
376 377 378 |
} return 0; |
7df3e59c3
|
379 380 381 |
error_cipher: crypto_free_skcipher(big_key_skcipher); error_rng: |
13100a72f
|
382 |
crypto_free_rng(big_key_rng); |
13100a72f
|
383 384 |
return ret; } |
7df3e59c3
|
385 |
late_initcall(big_key_init); |