Blame view

security/keys/big_key.c 8.5 KB
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: Sort out bi...
11
  #define pr_fmt(fmt) "big_key: "fmt
ab3c3587f   David Howells   KEYS: Implement a...
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   Kirill Marinushkin   Security: Keys: B...
17
  #include <linux/scatterlist.h>
ab3c3587f   David Howells   KEYS: Implement a...
18
19
  #include <keys/user-type.h>
  #include <keys/big_key-type.h>
13100a72f   Kirill Marinushkin   Security: Keys: B...
20
  #include <crypto/rng.h>
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
21
  #include <crypto/skcipher.h>
ab3c3587f   David Howells   KEYS: Implement a...
22

ab3c3587f   David Howells   KEYS: Implement a...
23
  /*
146aa8b14   David Howells   KEYS: Merge the t...
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   Kirill Marinushkin   Security: Keys: B...
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   David Howells   KEYS: Implement a...
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   Kirill Marinushkin   Security: Keys: B...
49
50
51
52
53
   * Key size for big_key data encryption
   */
  #define ENC_KEY_SIZE	16
  
  /*
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: big_key: Us...
59
60
61
  	.preparse		= big_key_preparse,
  	.free_preparse		= big_key_free_preparse,
  	.instantiate		= generic_key_instantiate,
ab3c3587f   David Howells   KEYS: Implement a...
62
63
64
65
66
67
68
  	.revoke			= big_key_revoke,
  	.destroy		= big_key_destroy,
  	.describe		= big_key_describe,
  	.read			= big_key_read,
  };
  
  /*
13100a72f   Kirill Marinushkin   Security: Keys: B...
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   Herbert Xu   KEYS: Use skciphe...
78
  static struct crypto_skcipher *big_key_skcipher;
13100a72f   Kirill Marinushkin   Security: Keys: B...
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   Herbert Xu   KEYS: Use skciphe...
95
  	SKCIPHER_REQUEST_ON_STACK(req, big_key_skcipher);
13100a72f   Kirill Marinushkin   Security: Keys: B...
96

d56d72c6a   Herbert Xu   KEYS: Use skciphe...
97
  	if (crypto_skcipher_setkey(big_key_skcipher, key, ENC_KEY_SIZE)) {
13100a72f   Kirill Marinushkin   Security: Keys: B...
98
99
100
  		ret = -EAGAIN;
  		goto error;
  	}
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
101
102
103
  	skcipher_request_set_tfm(req, big_key_skcipher);
  	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
  				      NULL, NULL);
13100a72f   Kirill Marinushkin   Security: Keys: B...
104
105
  
  	sg_init_one(&sgio, data, datalen);
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
106
  	skcipher_request_set_crypt(req, &sgio, &sgio, datalen, NULL);
13100a72f   Kirill Marinushkin   Security: Keys: B...
107
108
  
  	if (op == BIG_KEY_ENC)
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
109
  		ret = crypto_skcipher_encrypt(req);
13100a72f   Kirill Marinushkin   Security: Keys: B...
110
  	else
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
111
112
113
  		ret = crypto_skcipher_decrypt(req);
  
  	skcipher_request_zero(req);
13100a72f   Kirill Marinushkin   Security: Keys: B...
114
115
116
117
118
119
  
  error:
  	return ret;
  }
  
  /*
002edaf76   David Howells   KEYS: big_key: Us...
120
   * Preparse a big key
ab3c3587f   David Howells   KEYS: Implement a...
121
   */
002edaf76   David Howells   KEYS: big_key: Us...
122
  int big_key_preparse(struct key_preparsed_payload *prep)
ab3c3587f   David Howells   KEYS: Implement a...
123
  {
146aa8b14   David Howells   KEYS: Merge the t...
124
  	struct path *path = (struct path *)&prep->payload.data[big_key_path];
ab3c3587f   David Howells   KEYS: Implement a...
125
  	struct file *file;
13100a72f   Kirill Marinushkin   Security: Keys: B...
126
127
  	u8 *enckey;
  	u8 *data = NULL;
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: big_key: Us...
137
  	prep->quotalen = 16;
ab3c3587f   David Howells   KEYS: Implement a...
138

146aa8b14   David Howells   KEYS: Merge the t...
139
  	prep->payload.data[big_key_len] = (void *)(unsigned long)datalen;
ab3c3587f   David Howells   KEYS: Implement a...
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   Kirill Marinushkin   Security: Keys: B...
145
  		 * File content is stored encrypted with randomly generated key.
ab3c3587f   David Howells   KEYS: Implement a...
146
  		 */
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
147
  		size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
13100a72f   Kirill Marinushkin   Security: Keys: B...
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   Wei Yongjun   KEYS: fix error r...
175
176
  		if (IS_ERR(file)) {
  			ret = PTR_ERR(file);
13100a72f   Kirill Marinushkin   Security: Keys: B...
177
  			goto err_enckey;
d2b869702   Wei Yongjun   KEYS: fix error r...
178
  		}
ab3c3587f   David Howells   KEYS: Implement a...
179

13100a72f   Kirill Marinushkin   Security: Keys: B...
180
181
  		written = kernel_write(file, data, enclen, 0);
  		if (written != enclen) {
97826c821   David Howells   KEYS: Fix error h...
182
  			ret = written;
ab3c3587f   David Howells   KEYS: Implement a...
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   Kirill Marinushkin   Security: Keys: B...
191
  		prep->payload.data[big_key_data] = enckey;
ab3c3587f   David Howells   KEYS: Implement a...
192
193
194
  		*path = file->f_path;
  		path_get(path);
  		fput(file);
13100a72f   Kirill Marinushkin   Security: Keys: B...
195
  		kfree(data);
ab3c3587f   David Howells   KEYS: Implement a...
196
197
198
  	} else {
  		/* Just store the data in a buffer */
  		void *data = kmalloc(datalen, GFP_KERNEL);
13100a72f   Kirill Marinushkin   Security: Keys: B...
199

002edaf76   David Howells   KEYS: big_key: Us...
200
201
  		if (!data)
  			return -ENOMEM;
ab3c3587f   David Howells   KEYS: Implement a...
202

146aa8b14   David Howells   KEYS: Merge the t...
203
204
  		prep->payload.data[big_key_data] = data;
  		memcpy(data, prep->data, prep->datalen);
ab3c3587f   David Howells   KEYS: Implement a...
205
206
207
208
209
  	}
  	return 0;
  
  err_fput:
  	fput(file);
13100a72f   Kirill Marinushkin   Security: Keys: B...
210
211
  err_enckey:
  	kfree(enckey);
ab3c3587f   David Howells   KEYS: Implement a...
212
  error:
13100a72f   Kirill Marinushkin   Security: Keys: B...
213
  	kfree(data);
ab3c3587f   David Howells   KEYS: Implement a...
214
215
216
217
  	return ret;
  }
  
  /*
002edaf76   David Howells   KEYS: big_key: Us...
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   David Howells   KEYS: Merge the t...
223
  		struct path *path = (struct path *)&prep->payload.data[big_key_path];
13100a72f   Kirill Marinushkin   Security: Keys: B...
224

002edaf76   David Howells   KEYS: big_key: Us...
225
  		path_put(path);
002edaf76   David Howells   KEYS: big_key: Us...
226
  	}
13100a72f   Kirill Marinushkin   Security: Keys: B...
227
  	kfree(prep->payload.data[big_key_data]);
002edaf76   David Howells   KEYS: big_key: Us...
228
229
230
  }
  
  /*
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: Merge the t...
236
  	struct path *path = (struct path *)&key->payload.data[big_key_path];
ab3c3587f   David Howells   KEYS: Implement a...
237
238
239
  
  	/* clear the quota */
  	key_payload_reserve(key, 0);
146aa8b14   David Howells   KEYS: Merge the t...
240
241
  	if (key_is_instantiated(key) &&
  	    (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: Merge the t...
250
  	size_t datalen = (size_t)key->payload.data[big_key_len];
13100a72f   Kirill Marinushkin   Security: Keys: B...
251
  	if (datalen > BIG_KEY_FILE_THRESHOLD) {
146aa8b14   David Howells   KEYS: Merge the t...
252
  		struct path *path = (struct path *)&key->payload.data[big_key_path];
13100a72f   Kirill Marinushkin   Security: Keys: B...
253

ab3c3587f   David Howells   KEYS: Implement a...
254
255
256
  		path_put(path);
  		path->mnt = NULL;
  		path->dentry = NULL;
ab3c3587f   David Howells   KEYS: Implement a...
257
  	}
13100a72f   Kirill Marinushkin   Security: Keys: B...
258
259
  	kfree(key->payload.data[big_key_data]);
  	key->payload.data[big_key_data] = NULL;
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: Merge the t...
267
  	size_t datalen = (size_t)key->payload.data[big_key_len];
ab3c3587f   David Howells   KEYS: Implement a...
268
269
270
271
  
  	seq_puts(m, key->description);
  
  	if (key_is_instantiated(key))
146aa8b14   David Howells   KEYS: Merge the t...
272
  		seq_printf(m, ": %zu [%s]",
ab3c3587f   David Howells   KEYS: Implement a...
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   David Howells   KEYS: Merge the t...
283
  	size_t datalen = (size_t)key->payload.data[big_key_len];
ab3c3587f   David Howells   KEYS: Implement a...
284
285
286
287
288
289
  	long ret;
  
  	if (!buffer || buflen < datalen)
  		return datalen;
  
  	if (datalen > BIG_KEY_FILE_THRESHOLD) {
146aa8b14   David Howells   KEYS: Merge the t...
290
  		struct path *path = (struct path *)&key->payload.data[big_key_path];
ab3c3587f   David Howells   KEYS: Implement a...
291
  		struct file *file;
13100a72f   Kirill Marinushkin   Security: Keys: B...
292
293
  		u8 *data;
  		u8 *enckey = (u8 *)key->payload.data[big_key_data];
d56d72c6a   Herbert Xu   KEYS: Use skciphe...
294
  		size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
13100a72f   Kirill Marinushkin   Security: Keys: B...
295
296
297
298
  
  		data = kmalloc(enclen, GFP_KERNEL);
  		if (!data)
  			return -ENOMEM;
ab3c3587f   David Howells   KEYS: Implement a...
299
300
  
  		file = dentry_open(path, O_RDONLY, current_cred());
13100a72f   Kirill Marinushkin   Security: Keys: B...
301
302
303
304
  		if (IS_ERR(file)) {
  			ret = PTR_ERR(file);
  			goto error;
  		}
ab3c3587f   David Howells   KEYS: Implement a...
305

13100a72f   Kirill Marinushkin   Security: Keys: B...
306
307
308
  		/* read file to kernel and decrypt */
  		ret = kernel_read(file, 0, data, enclen);
  		if (ret >= 0 && ret != enclen) {
ab3c3587f   David Howells   KEYS: Implement a...
309
  			ret = -EIO;
13100a72f   Kirill Marinushkin   Security: Keys: B...
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   David Howells   KEYS: Implement a...
327
328
  	} else {
  		ret = datalen;
146aa8b14   David Howells   KEYS: Merge the t...
329
330
  		if (copy_to_user(buffer, key->payload.data[big_key_data],
  				 datalen) != 0)
ab3c3587f   David Howells   KEYS: Implement a...
331
332
333
334
335
  			ret = -EFAULT;
  	}
  
  	return ret;
  }
13100a72f   Kirill Marinushkin   Security: Keys: B...
336
337
338
  /*
   * Register key type
   */
ab3c3587f   David Howells   KEYS: Implement a...
339
340
  static int __init big_key_init(void)
  {
7df3e59c3   David Howells   KEYS: Sort out bi...
341
342
343
  	struct crypto_skcipher *cipher;
  	struct crypto_rng *rng;
  	int ret;
13100a72f   Kirill Marinushkin   Security: Keys: B...
344

7df3e59c3   David Howells   KEYS: Sort out bi...
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   Kirill Marinushkin   Security: Keys: B...
350
  	}
7df3e59c3   David Howells   KEYS: Sort out bi...
351
  	big_key_rng = rng;
13100a72f   Kirill Marinushkin   Security: Keys: B...
352
  	/* seed RNG */
7df3e59c3   David Howells   KEYS: Sort out bi...
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   Kirill Marinushkin   Security: Keys: B...
359
360
  
  	/* init block cipher */
7df3e59c3   David Howells   KEYS: Sort out bi...
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   Kirill Marinushkin   Security: Keys: B...
376
377
378
  	}
  
  	return 0;
7df3e59c3   David Howells   KEYS: Sort out bi...
379
380
381
  error_cipher:
  	crypto_free_skcipher(big_key_skcipher);
  error_rng:
13100a72f   Kirill Marinushkin   Security: Keys: B...
382
  	crypto_free_rng(big_key_rng);
13100a72f   Kirill Marinushkin   Security: Keys: B...
383
384
  	return ret;
  }
7df3e59c3   David Howells   KEYS: Sort out bi...
385
  late_initcall(big_key_init);