Blame view

fs/crypto/keyinfo.c 8.52 KB
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
1
  /*
0b81d0779   Jaegeuk Kim   fs crypto: move p...
2
   * key management facility for FS encryption support.
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
3
4
5
   *
   * Copyright (C) 2015, Google, Inc.
   *
0b81d0779   Jaegeuk Kim   fs crypto: move p...
6
   * This contains encryption key functions.
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
7
8
9
   *
   * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015.
   */
0b81d0779   Jaegeuk Kim   fs crypto: move p...
10

0adda907f   Jaegeuk Kim   f2fs crypto: add ...
11
  #include <keys/user-type.h>
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
12
  #include <linux/scatterlist.h>
0b81d0779   Jaegeuk Kim   fs crypto: move p...
13
  #include <linux/fscrypto.h>
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
14
15
16
  
  static void derive_crypt_complete(struct crypto_async_request *req, int rc)
  {
0b81d0779   Jaegeuk Kim   fs crypto: move p...
17
  	struct fscrypt_completion_result *ecr = req->data;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
18
19
20
21
22
23
24
25
26
  
  	if (rc == -EINPROGRESS)
  		return;
  
  	ecr->res = rc;
  	complete(&ecr->completion);
  }
  
  /**
0b81d0779   Jaegeuk Kim   fs crypto: move p...
27
   * derive_key_aes() - Derive a key using AES-128-ECB
0fac2d501   Jaegeuk Kim   f2fs crypto: fix ...
28
   * @deriving_key: Encryption key used for derivation.
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
29
30
31
32
33
   * @source_key:   Source key to which to apply derivation.
   * @derived_key:  Derived key.
   *
   * Return: Zero on success; non-zero otherwise.
   */
0b81d0779   Jaegeuk Kim   fs crypto: move p...
34
35
36
  static int derive_key_aes(u8 deriving_key[FS_AES_128_ECB_KEY_SIZE],
  				u8 source_key[FS_AES_256_XTS_KEY_SIZE],
  				u8 derived_key[FS_AES_256_XTS_KEY_SIZE])
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
37
38
  {
  	int res = 0;
d407574e7   Linus Torvalds   Merge tag 'for-f2...
39
  	struct skcipher_request *req = NULL;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
40
  	DECLARE_FS_COMPLETION_RESULT(ecr);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
41
  	struct scatterlist src_sg, dst_sg;
d407574e7   Linus Torvalds   Merge tag 'for-f2...
42
  	struct crypto_skcipher *tfm = crypto_alloc_skcipher("ecb(aes)", 0, 0);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
43
44
45
46
47
48
  
  	if (IS_ERR(tfm)) {
  		res = PTR_ERR(tfm);
  		tfm = NULL;
  		goto out;
  	}
d407574e7   Linus Torvalds   Merge tag 'for-f2...
49
50
  	crypto_skcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY);
  	req = skcipher_request_alloc(tfm, GFP_NOFS);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
51
52
53
54
  	if (!req) {
  		res = -ENOMEM;
  		goto out;
  	}
d407574e7   Linus Torvalds   Merge tag 'for-f2...
55
  	skcipher_request_set_callback(req,
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
56
57
  			CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP,
  			derive_crypt_complete, &ecr);
d407574e7   Linus Torvalds   Merge tag 'for-f2...
58
  	res = crypto_skcipher_setkey(tfm, deriving_key,
0b81d0779   Jaegeuk Kim   fs crypto: move p...
59
  					FS_AES_128_ECB_KEY_SIZE);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
60
61
  	if (res < 0)
  		goto out;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
62
63
  	sg_init_one(&src_sg, source_key, FS_AES_256_XTS_KEY_SIZE);
  	sg_init_one(&dst_sg, derived_key, FS_AES_256_XTS_KEY_SIZE);
d407574e7   Linus Torvalds   Merge tag 'for-f2...
64
  	skcipher_request_set_crypt(req, &src_sg, &dst_sg,
0b81d0779   Jaegeuk Kim   fs crypto: move p...
65
  					FS_AES_256_XTS_KEY_SIZE, NULL);
d407574e7   Linus Torvalds   Merge tag 'for-f2...
66
  	res = crypto_skcipher_encrypt(req);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
67
  	if (res == -EINPROGRESS || res == -EBUSY) {
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
68
69
70
71
  		wait_for_completion(&ecr.completion);
  		res = ecr.res;
  	}
  out:
d407574e7   Linus Torvalds   Merge tag 'for-f2...
72
73
  	skcipher_request_free(req);
  	crypto_free_skcipher(tfm);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
74
75
  	return res;
  }
b5a7aef1e   Jaegeuk Kim   fscrypto/f2fs: al...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  static int validate_user_key(struct fscrypt_info *crypt_info,
  			struct fscrypt_context *ctx, u8 *raw_key,
  			u8 *prefix, int prefix_size)
  {
  	u8 *full_key_descriptor;
  	struct key *keyring_key;
  	struct fscrypt_key *master_key;
  	const struct user_key_payload *ukp;
  	int full_key_len = prefix_size + (FS_KEY_DESCRIPTOR_SIZE * 2) + 1;
  	int res;
  
  	full_key_descriptor = kmalloc(full_key_len, GFP_NOFS);
  	if (!full_key_descriptor)
  		return -ENOMEM;
  
  	memcpy(full_key_descriptor, prefix, prefix_size);
  	sprintf(full_key_descriptor + prefix_size,
  			"%*phN", FS_KEY_DESCRIPTOR_SIZE,
  			ctx->master_key_descriptor);
  	full_key_descriptor[full_key_len - 1] = '\0';
  	keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL);
  	kfree(full_key_descriptor);
  	if (IS_ERR(keyring_key))
  		return PTR_ERR(keyring_key);
  
  	if (keyring_key->type != &key_type_logon) {
  		printk_once(KERN_WARNING
  				"%s: key type must be logon
  ", __func__);
  		res = -ENOKEY;
  		goto out;
  	}
  	down_read(&keyring_key->sem);
  	ukp = user_key_payload(keyring_key);
  	if (ukp->datalen != sizeof(struct fscrypt_key)) {
  		res = -EINVAL;
  		up_read(&keyring_key->sem);
  		goto out;
  	}
  	master_key = (struct fscrypt_key *)ukp->data;
  	BUILD_BUG_ON(FS_AES_128_ECB_KEY_SIZE != FS_KEY_DERIVATION_NONCE_SIZE);
  
  	if (master_key->size != FS_AES_256_XTS_KEY_SIZE) {
  		printk_once(KERN_WARNING
  				"%s: key size incorrect: %d
  ",
  				__func__, master_key->size);
  		res = -ENOKEY;
  		up_read(&keyring_key->sem);
  		goto out;
  	}
  	res = derive_key_aes(ctx->nonce, master_key->raw, raw_key);
  	up_read(&keyring_key->sem);
  	if (res)
  		goto out;
  
  	crypt_info->ci_keyring_key = keyring_key;
  	return 0;
  out:
  	key_put(keyring_key);
  	return res;
  }
8f39850df   Eric Biggers   fscrypto: improve...
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
  static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode,
  				 const char **cipher_str_ret, int *keysize_ret)
  {
  	if (S_ISREG(inode->i_mode)) {
  		if (ci->ci_data_mode == FS_ENCRYPTION_MODE_AES_256_XTS) {
  			*cipher_str_ret = "xts(aes)";
  			*keysize_ret = FS_AES_256_XTS_KEY_SIZE;
  			return 0;
  		}
  		pr_warn_once("fscrypto: unsupported contents encryption mode "
  			     "%d for inode %lu
  ",
  			     ci->ci_data_mode, inode->i_ino);
  		return -ENOKEY;
  	}
  
  	if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) {
  		if (ci->ci_filename_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
  			*cipher_str_ret = "cts(cbc(aes))";
  			*keysize_ret = FS_AES_256_CTS_KEY_SIZE;
  			return 0;
  		}
  		pr_warn_once("fscrypto: unsupported filenames encryption mode "
  			     "%d for inode %lu
  ",
  			     ci->ci_filename_mode, inode->i_ino);
  		return -ENOKEY;
  	}
  
  	pr_warn_once("fscrypto: unsupported file type %d for inode %lu
  ",
  		     (inode->i_mode & S_IFMT), inode->i_ino);
  	return -ENOKEY;
  }
0b81d0779   Jaegeuk Kim   fs crypto: move p...
172
  static void put_crypt_info(struct fscrypt_info *ci)
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
173
  {
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
174
175
  	if (!ci)
  		return;
d407574e7   Linus Torvalds   Merge tag 'for-f2...
176
177
  	key_put(ci->ci_keyring_key);
  	crypto_free_skcipher(ci->ci_ctfm);
0b81d0779   Jaegeuk Kim   fs crypto: move p...
178
  	kmem_cache_free(fscrypt_info_cachep, ci);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
179
  }
0b81d0779   Jaegeuk Kim   fs crypto: move p...
180
  int get_crypt_info(struct inode *inode)
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
181
  {
0b81d0779   Jaegeuk Kim   fs crypto: move p...
182
  	struct fscrypt_info *crypt_info;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
183
  	struct fscrypt_context ctx;
d407574e7   Linus Torvalds   Merge tag 'for-f2...
184
  	struct crypto_skcipher *ctfm;
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
185
  	const char *cipher_str;
8f39850df   Eric Biggers   fscrypto: improve...
186
  	int keysize;
0f0909e24   Eric Biggers   fscrypto: don't u...
187
  	u8 *raw_key = NULL;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
188
  	int res;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
189
  	res = fscrypt_initialize();
cfc4d971d   Jaegeuk Kim   f2fs crypto: spli...
190
191
  	if (res)
  		return res;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
192
193
194
  
  	if (!inode->i_sb->s_cop->get_context)
  		return -EOPNOTSUPP;
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
195
  retry:
0b81d0779   Jaegeuk Kim   fs crypto: move p...
196
  	crypt_info = ACCESS_ONCE(inode->i_crypt_info);
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
197
198
199
  	if (crypt_info) {
  		if (!crypt_info->ci_keyring_key ||
  				key_validate(crypt_info->ci_keyring_key) == 0)
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
200
  			return 0;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
201
  		fscrypt_put_encryption_info(inode, crypt_info);
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
202
  		goto retry;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
203
  	}
0b81d0779   Jaegeuk Kim   fs crypto: move p...
204
205
206
207
  	res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx));
  	if (res < 0) {
  		if (!fscrypt_dummy_context_enabled(inode))
  			return res;
8f39850df   Eric Biggers   fscrypto: improve...
208
  		ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
209
210
211
212
  		ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
  		ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
  		ctx.flags = 0;
  	} else if (res != sizeof(ctx)) {
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
213
  		return -EINVAL;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
214
  	}
8f39850df   Eric Biggers   fscrypto: improve...
215
216
217
218
219
220
  
  	if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1)
  		return -EINVAL;
  
  	if (ctx.flags & ~FS_POLICY_FLAGS_VALID)
  		return -EINVAL;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
221

0b81d0779   Jaegeuk Kim   fs crypto: move p...
222
  	crypt_info = kmem_cache_alloc(fscrypt_info_cachep, GFP_NOFS);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
223
224
225
226
227
228
229
  	if (!crypt_info)
  		return -ENOMEM;
  
  	crypt_info->ci_flags = ctx.flags;
  	crypt_info->ci_data_mode = ctx.contents_encryption_mode;
  	crypt_info->ci_filename_mode = ctx.filenames_encryption_mode;
  	crypt_info->ci_ctfm = NULL;
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
230
  	crypt_info->ci_keyring_key = NULL;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
231
232
  	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
  				sizeof(crypt_info->ci_master_key));
640778fbc   Jaegeuk Kim   f2fs crypto: get ...
233

8f39850df   Eric Biggers   fscrypto: improve...
234
235
  	res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
  	if (res)
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
236
  		goto out;
8f39850df   Eric Biggers   fscrypto: improve...
237

0f0909e24   Eric Biggers   fscrypto: don't u...
238
239
240
241
242
243
244
245
  	/*
  	 * This cannot be a stack buffer because it is passed to the scatterlist
  	 * crypto API as part of key derivation.
  	 */
  	res = -ENOMEM;
  	raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
  	if (!raw_key)
  		goto out;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
246
247
248
249
  	if (fscrypt_dummy_context_enabled(inode)) {
  		memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
  		goto got_key;
  	}
0b81d0779   Jaegeuk Kim   fs crypto: move p...
250

b5a7aef1e   Jaegeuk Kim   fscrypto/f2fs: al...
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
  	res = validate_user_key(crypt_info, &ctx, raw_key,
  			FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
  	if (res && inode->i_sb->s_cop->key_prefix) {
  		u8 *prefix = NULL;
  		int prefix_size, res2;
  
  		prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
  		res2 = validate_user_key(crypt_info, &ctx, raw_key,
  							prefix, prefix_size);
  		if (res2) {
  			if (res2 == -ENOKEY)
  				res = -ENOKEY;
  			goto out;
  		}
  	} else if (res) {
66aa3e127   Jaegeuk Kim   f2fs crypto: repl...
266
267
  		goto out;
  	}
0b81d0779   Jaegeuk Kim   fs crypto: move p...
268
  got_key:
d407574e7   Linus Torvalds   Merge tag 'for-f2...
269
  	ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
270
271
272
273
274
275
276
  	if (!ctfm || IS_ERR(ctfm)) {
  		res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
  		printk(KERN_DEBUG
  		       "%s: error %d (inode %u) allocating crypto tfm
  ",
  		       __func__, res, (unsigned) inode->i_ino);
  		goto out;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
277
  	}
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
278
  	crypt_info->ci_ctfm = ctfm;
d407574e7   Linus Torvalds   Merge tag 'for-f2...
279
280
  	crypto_skcipher_clear_flags(ctfm, ~0);
  	crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
8f39850df   Eric Biggers   fscrypto: improve...
281
  	res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
282
283
  	if (res)
  		goto out;
0f0909e24   Eric Biggers   fscrypto: don't u...
284
285
  	kzfree(raw_key);
  	raw_key = NULL;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
286
287
  	if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) != NULL) {
  		put_crypt_info(crypt_info);
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
288
289
290
291
292
  		goto retry;
  	}
  	return 0;
  
  out:
0b81d0779   Jaegeuk Kim   fs crypto: move p...
293
  	if (res == -ENOKEY)
26bf3dc7e   Jaegeuk Kim   f2fs crypto: use ...
294
  		res = 0;
0b81d0779   Jaegeuk Kim   fs crypto: move p...
295
  	put_crypt_info(crypt_info);
0f0909e24   Eric Biggers   fscrypto: don't u...
296
  	kzfree(raw_key);
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
297
298
  	return res;
  }
0b81d0779   Jaegeuk Kim   fs crypto: move p...
299
  void fscrypt_put_encryption_info(struct inode *inode, struct fscrypt_info *ci)
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
300
  {
0b81d0779   Jaegeuk Kim   fs crypto: move p...
301
302
303
304
305
306
  	struct fscrypt_info *prev;
  
  	if (ci == NULL)
  		ci = ACCESS_ONCE(inode->i_crypt_info);
  	if (ci == NULL)
  		return;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
307

0b81d0779   Jaegeuk Kim   fs crypto: move p...
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  	prev = cmpxchg(&inode->i_crypt_info, ci, NULL);
  	if (prev != ci)
  		return;
  
  	put_crypt_info(ci);
  }
  EXPORT_SYMBOL(fscrypt_put_encryption_info);
  
  int fscrypt_get_encryption_info(struct inode *inode)
  {
  	struct fscrypt_info *ci = inode->i_crypt_info;
  
  	if (!ci ||
  		(ci->ci_keyring_key &&
  		 (ci->ci_keyring_key->flags & ((1 << KEY_FLAG_INVALIDATED) |
  					       (1 << KEY_FLAG_REVOKED) |
  					       (1 << KEY_FLAG_DEAD)))))
  		return get_crypt_info(inode);
  	return 0;
0adda907f   Jaegeuk Kim   f2fs crypto: add ...
327
  }
0b81d0779   Jaegeuk Kim   fs crypto: move p...
328
  EXPORT_SYMBOL(fscrypt_get_encryption_info);