Commit | Line | Data |
---|---|---|
d5d0e8c7 MH |
1 | /* |
2 | * linux/fs/ext4/crypto_fname.c | |
3 | * | |
4 | * Copyright (C) 2015, Google, Inc. | |
5 | * | |
6 | * This contains functions for filename crypto management in ext4 | |
7 | * | |
8 | * Written by Uday Savagaonkar, 2014. | |
9 | * | |
10 | * This has not yet undergone a rigorous security audit. | |
11 | * | |
12 | */ | |
13 | ||
3f32a5be | 14 | #include <crypto/skcipher.h> |
d5d0e8c7 MH |
15 | #include <keys/encrypted-type.h> |
16 | #include <keys/user-type.h> | |
d5d0e8c7 MH |
17 | #include <linux/gfp.h> |
18 | #include <linux/kernel.h> | |
19 | #include <linux/key.h> | |
d5d0e8c7 MH |
20 | #include <linux/list.h> |
21 | #include <linux/mempool.h> | |
22 | #include <linux/random.h> | |
23 | #include <linux/scatterlist.h> | |
24 | #include <linux/spinlock_types.h> | |
25 | ||
26 | #include "ext4.h" | |
27 | #include "ext4_crypto.h" | |
28 | #include "xattr.h" | |
29 | ||
30 | /** | |
31 | * ext4_dir_crypt_complete() - | |
32 | */ | |
33 | static void ext4_dir_crypt_complete(struct crypto_async_request *req, int res) | |
34 | { | |
35 | struct ext4_completion_result *ecr = req->data; | |
36 | ||
37 | if (res == -EINPROGRESS) | |
38 | return; | |
39 | ecr->res = res; | |
40 | complete(&ecr->completion); | |
41 | } | |
42 | ||
43 | bool ext4_valid_filenames_enc_mode(uint32_t mode) | |
44 | { | |
45 | return (mode == EXT4_ENCRYPTION_MODE_AES_256_CTS); | |
46 | } | |
47 | ||
b7236e21 TT |
48 | static unsigned max_name_len(struct inode *inode) |
49 | { | |
50 | return S_ISLNK(inode->i_mode) ? inode->i_sb->s_blocksize : | |
51 | EXT4_NAME_LEN; | |
52 | } | |
53 | ||
d5d0e8c7 MH |
54 | /** |
55 | * ext4_fname_encrypt() - | |
56 | * | |
57 | * This function encrypts the input filename, and returns the length of the | |
58 | * ciphertext. Errors are returned as negative numbers. We trust the caller to | |
59 | * allocate sufficient memory to oname string. | |
60 | */ | |
b7236e21 | 61 | static int ext4_fname_encrypt(struct inode *inode, |
d5d0e8c7 MH |
62 | const struct qstr *iname, |
63 | struct ext4_str *oname) | |
64 | { | |
65 | u32 ciphertext_len; | |
3f32a5be | 66 | struct skcipher_request *req = NULL; |
d5d0e8c7 | 67 | DECLARE_EXT4_COMPLETION_RESULT(ecr); |
b7236e21 | 68 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; |
3f32a5be | 69 | struct crypto_skcipher *tfm = ci->ci_ctfm; |
d5d0e8c7 MH |
70 | int res = 0; |
71 | char iv[EXT4_CRYPTO_BLOCK_SIZE]; | |
d2299590 | 72 | struct scatterlist src_sg, dst_sg; |
b7236e21 | 73 | int padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK); |
d2299590 | 74 | char *workbuf, buf[32], *alloc_buf = NULL; |
b7236e21 | 75 | unsigned lim = max_name_len(inode); |
d5d0e8c7 | 76 | |
b7236e21 | 77 | if (iname->len <= 0 || iname->len > lim) |
d5d0e8c7 MH |
78 | return -EIO; |
79 | ||
80 | ciphertext_len = (iname->len < EXT4_CRYPTO_BLOCK_SIZE) ? | |
81 | EXT4_CRYPTO_BLOCK_SIZE : iname->len; | |
a44cd7a0 | 82 | ciphertext_len = ext4_fname_crypto_round_up(ciphertext_len, padding); |
b7236e21 TT |
83 | ciphertext_len = (ciphertext_len > lim) |
84 | ? lim : ciphertext_len; | |
d5d0e8c7 | 85 | |
d2299590 TT |
86 | if (ciphertext_len <= sizeof(buf)) { |
87 | workbuf = buf; | |
88 | } else { | |
89 | alloc_buf = kmalloc(ciphertext_len, GFP_NOFS); | |
90 | if (!alloc_buf) | |
91 | return -ENOMEM; | |
92 | workbuf = alloc_buf; | |
93 | } | |
94 | ||
d5d0e8c7 | 95 | /* Allocate request */ |
3f32a5be | 96 | req = skcipher_request_alloc(tfm, GFP_NOFS); |
d5d0e8c7 MH |
97 | if (!req) { |
98 | printk_ratelimited( | |
99 | KERN_ERR "%s: crypto_request_alloc() failed\n", __func__); | |
d2299590 | 100 | kfree(alloc_buf); |
d5d0e8c7 MH |
101 | return -ENOMEM; |
102 | } | |
3f32a5be | 103 | skcipher_request_set_callback(req, |
d5d0e8c7 MH |
104 | CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, |
105 | ext4_dir_crypt_complete, &ecr); | |
106 | ||
d5d0e8c7 MH |
107 | /* Copy the input */ |
108 | memcpy(workbuf, iname->name, iname->len); | |
109 | if (iname->len < ciphertext_len) | |
110 | memset(workbuf + iname->len, 0, ciphertext_len - iname->len); | |
111 | ||
112 | /* Initialize IV */ | |
113 | memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE); | |
114 | ||
115 | /* Create encryption request */ | |
d2299590 TT |
116 | sg_init_one(&src_sg, workbuf, ciphertext_len); |
117 | sg_init_one(&dst_sg, oname->name, ciphertext_len); | |
3f32a5be HX |
118 | skcipher_request_set_crypt(req, &src_sg, &dst_sg, ciphertext_len, iv); |
119 | res = crypto_skcipher_encrypt(req); | |
d5d0e8c7 | 120 | if (res == -EINPROGRESS || res == -EBUSY) { |
d5d0e8c7 MH |
121 | wait_for_completion(&ecr.completion); |
122 | res = ecr.res; | |
123 | } | |
d2299590 | 124 | kfree(alloc_buf); |
3f32a5be | 125 | skcipher_request_free(req); |
d5d0e8c7 MH |
126 | if (res < 0) { |
127 | printk_ratelimited( | |
128 | KERN_ERR "%s: Error (error code %d)\n", __func__, res); | |
129 | } | |
130 | oname->len = ciphertext_len; | |
131 | return res; | |
132 | } | |
133 | ||
134 | /* | |
135 | * ext4_fname_decrypt() | |
136 | * This function decrypts the input filename, and returns | |
137 | * the length of the plaintext. | |
138 | * Errors are returned as negative numbers. | |
139 | * We trust the caller to allocate sufficient memory to oname string. | |
140 | */ | |
b7236e21 | 141 | static int ext4_fname_decrypt(struct inode *inode, |
d5d0e8c7 MH |
142 | const struct ext4_str *iname, |
143 | struct ext4_str *oname) | |
144 | { | |
145 | struct ext4_str tmp_in[2], tmp_out[1]; | |
3f32a5be | 146 | struct skcipher_request *req = NULL; |
d5d0e8c7 | 147 | DECLARE_EXT4_COMPLETION_RESULT(ecr); |
d2299590 | 148 | struct scatterlist src_sg, dst_sg; |
b7236e21 | 149 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; |
3f32a5be | 150 | struct crypto_skcipher *tfm = ci->ci_ctfm; |
d5d0e8c7 MH |
151 | int res = 0; |
152 | char iv[EXT4_CRYPTO_BLOCK_SIZE]; | |
b7236e21 | 153 | unsigned lim = max_name_len(inode); |
d5d0e8c7 | 154 | |
b7236e21 | 155 | if (iname->len <= 0 || iname->len > lim) |
d5d0e8c7 MH |
156 | return -EIO; |
157 | ||
158 | tmp_in[0].name = iname->name; | |
159 | tmp_in[0].len = iname->len; | |
160 | tmp_out[0].name = oname->name; | |
161 | ||
162 | /* Allocate request */ | |
3f32a5be | 163 | req = skcipher_request_alloc(tfm, GFP_NOFS); |
d5d0e8c7 MH |
164 | if (!req) { |
165 | printk_ratelimited( | |
166 | KERN_ERR "%s: crypto_request_alloc() failed\n", __func__); | |
167 | return -ENOMEM; | |
168 | } | |
3f32a5be | 169 | skcipher_request_set_callback(req, |
d5d0e8c7 MH |
170 | CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, |
171 | ext4_dir_crypt_complete, &ecr); | |
172 | ||
d5d0e8c7 MH |
173 | /* Initialize IV */ |
174 | memset(iv, 0, EXT4_CRYPTO_BLOCK_SIZE); | |
175 | ||
176 | /* Create encryption request */ | |
d2299590 TT |
177 | sg_init_one(&src_sg, iname->name, iname->len); |
178 | sg_init_one(&dst_sg, oname->name, oname->len); | |
3f32a5be HX |
179 | skcipher_request_set_crypt(req, &src_sg, &dst_sg, iname->len, iv); |
180 | res = crypto_skcipher_decrypt(req); | |
d5d0e8c7 | 181 | if (res == -EINPROGRESS || res == -EBUSY) { |
d5d0e8c7 MH |
182 | wait_for_completion(&ecr.completion); |
183 | res = ecr.res; | |
184 | } | |
3f32a5be | 185 | skcipher_request_free(req); |
d5d0e8c7 MH |
186 | if (res < 0) { |
187 | printk_ratelimited( | |
188 | KERN_ERR "%s: Error in ext4_fname_encrypt (error code %d)\n", | |
189 | __func__, res); | |
190 | return res; | |
191 | } | |
192 | ||
193 | oname->len = strnlen(oname->name, iname->len); | |
194 | return oname->len; | |
195 | } | |
196 | ||
5de0b4d0 TT |
197 | static const char *lookup_table = |
198 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; | |
199 | ||
d5d0e8c7 MH |
200 | /** |
201 | * ext4_fname_encode_digest() - | |
202 | * | |
203 | * Encodes the input digest using characters from the set [a-zA-Z0-9_+]. | |
204 | * The encoded string is roughly 4/3 times the size of the input string. | |
205 | */ | |
5de0b4d0 | 206 | static int digest_encode(const char *src, int len, char *dst) |
d5d0e8c7 | 207 | { |
5de0b4d0 TT |
208 | int i = 0, bits = 0, ac = 0; |
209 | char *cp = dst; | |
210 | ||
211 | while (i < len) { | |
212 | ac += (((unsigned char) src[i]) << bits); | |
213 | bits += 8; | |
214 | do { | |
215 | *cp++ = lookup_table[ac & 0x3f]; | |
216 | ac >>= 6; | |
217 | bits -= 6; | |
218 | } while (bits >= 6); | |
d5d0e8c7 MH |
219 | i++; |
220 | } | |
5de0b4d0 TT |
221 | if (bits) |
222 | *cp++ = lookup_table[ac & 0x3f]; | |
223 | return cp - dst; | |
d5d0e8c7 MH |
224 | } |
225 | ||
5de0b4d0 | 226 | static int digest_decode(const char *src, int len, char *dst) |
d5d0e8c7 | 227 | { |
5de0b4d0 TT |
228 | int i = 0, bits = 0, ac = 0; |
229 | const char *p; | |
230 | char *cp = dst; | |
231 | ||
232 | while (i < len) { | |
233 | p = strchr(lookup_table, src[i]); | |
234 | if (p == NULL || src[i] == 0) | |
235 | return -2; | |
236 | ac += (p - lookup_table) << bits; | |
237 | bits += 6; | |
238 | if (bits >= 8) { | |
239 | *cp++ = ac & 0xff; | |
240 | ac >>= 8; | |
241 | bits -= 8; | |
242 | } | |
243 | i++; | |
d5d0e8c7 | 244 | } |
5de0b4d0 TT |
245 | if (ac) |
246 | return -1; | |
247 | return cp - dst; | |
d5d0e8c7 MH |
248 | } |
249 | ||
d5d0e8c7 MH |
250 | /** |
251 | * ext4_fname_crypto_round_up() - | |
252 | * | |
253 | * Return: The next multiple of block size | |
254 | */ | |
255 | u32 ext4_fname_crypto_round_up(u32 size, u32 blksize) | |
256 | { | |
257 | return ((size+blksize-1)/blksize)*blksize; | |
258 | } | |
259 | ||
4d3c4e5b TT |
260 | unsigned ext4_fname_encrypted_size(struct inode *inode, u32 ilen) |
261 | { | |
262 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; | |
263 | int padding = 32; | |
264 | ||
265 | if (ci) | |
266 | padding = 4 << (ci->ci_flags & EXT4_POLICY_FLAGS_PAD_MASK); | |
267 | if (ilen < EXT4_CRYPTO_BLOCK_SIZE) | |
268 | ilen = EXT4_CRYPTO_BLOCK_SIZE; | |
269 | return ext4_fname_crypto_round_up(ilen, padding); | |
270 | } | |
271 | ||
272 | /* | |
273 | * ext4_fname_crypto_alloc_buffer() - | |
d5d0e8c7 MH |
274 | * |
275 | * Allocates an output buffer that is sufficient for the crypto operation | |
276 | * specified by the context and the direction. | |
277 | */ | |
b7236e21 | 278 | int ext4_fname_crypto_alloc_buffer(struct inode *inode, |
d5d0e8c7 MH |
279 | u32 ilen, struct ext4_str *crypto_str) |
280 | { | |
4d3c4e5b | 281 | unsigned int olen = ext4_fname_encrypted_size(inode, ilen); |
d5d0e8c7 | 282 | |
d5d0e8c7 MH |
283 | crypto_str->len = olen; |
284 | if (olen < EXT4_FNAME_CRYPTO_DIGEST_SIZE*2) | |
285 | olen = EXT4_FNAME_CRYPTO_DIGEST_SIZE*2; | |
286 | /* Allocated buffer can hold one more character to null-terminate the | |
287 | * string */ | |
288 | crypto_str->name = kmalloc(olen+1, GFP_NOFS); | |
289 | if (!(crypto_str->name)) | |
290 | return -ENOMEM; | |
291 | return 0; | |
292 | } | |
293 | ||
294 | /** | |
295 | * ext4_fname_crypto_free_buffer() - | |
296 | * | |
297 | * Frees the buffer allocated for crypto operation. | |
298 | */ | |
299 | void ext4_fname_crypto_free_buffer(struct ext4_str *crypto_str) | |
300 | { | |
301 | if (!crypto_str) | |
302 | return; | |
303 | kfree(crypto_str->name); | |
304 | crypto_str->name = NULL; | |
305 | } | |
306 | ||
307 | /** | |
308 | * ext4_fname_disk_to_usr() - converts a filename from disk space to user space | |
309 | */ | |
b7236e21 | 310 | int _ext4_fname_disk_to_usr(struct inode *inode, |
5de0b4d0 TT |
311 | struct dx_hash_info *hinfo, |
312 | const struct ext4_str *iname, | |
313 | struct ext4_str *oname) | |
d5d0e8c7 | 314 | { |
5de0b4d0 TT |
315 | char buf[24]; |
316 | int ret; | |
317 | ||
d5d0e8c7 MH |
318 | if (iname->len < 3) { |
319 | /*Check for . and .. */ | |
320 | if (iname->name[0] == '.' && iname->name[iname->len-1] == '.') { | |
321 | oname->name[0] = '.'; | |
322 | oname->name[iname->len-1] = '.'; | |
323 | oname->len = iname->len; | |
324 | return oname->len; | |
325 | } | |
326 | } | |
27977b69 TT |
327 | if (iname->len < EXT4_CRYPTO_BLOCK_SIZE) { |
328 | EXT4_ERROR_INODE(inode, "encrypted inode too small"); | |
329 | return -EUCLEAN; | |
330 | } | |
b7236e21 TT |
331 | if (EXT4_I(inode)->i_crypt_info) |
332 | return ext4_fname_decrypt(inode, iname, oname); | |
5de0b4d0 TT |
333 | |
334 | if (iname->len <= EXT4_FNAME_CRYPTO_DIGEST_SIZE) { | |
335 | ret = digest_encode(iname->name, iname->len, oname->name); | |
336 | oname->len = ret; | |
337 | return ret; | |
338 | } | |
339 | if (hinfo) { | |
340 | memcpy(buf, &hinfo->hash, 4); | |
341 | memcpy(buf+4, &hinfo->minor_hash, 4); | |
342 | } else | |
343 | memset(buf, 0, 8); | |
344 | memcpy(buf + 8, iname->name + iname->len - 16, 16); | |
345 | oname->name[0] = '_'; | |
346 | ret = digest_encode(buf, 24, oname->name+1); | |
347 | oname->len = ret + 1; | |
348 | return ret + 1; | |
d5d0e8c7 MH |
349 | } |
350 | ||
b7236e21 | 351 | int ext4_fname_disk_to_usr(struct inode *inode, |
5de0b4d0 | 352 | struct dx_hash_info *hinfo, |
d5d0e8c7 MH |
353 | const struct ext4_dir_entry_2 *de, |
354 | struct ext4_str *oname) | |
355 | { | |
356 | struct ext4_str iname = {.name = (unsigned char *) de->name, | |
357 | .len = de->name_len }; | |
358 | ||
b7236e21 | 359 | return _ext4_fname_disk_to_usr(inode, hinfo, &iname, oname); |
d5d0e8c7 MH |
360 | } |
361 | ||
362 | ||
363 | /** | |
364 | * ext4_fname_usr_to_disk() - converts a filename from user space to disk space | |
365 | */ | |
b7236e21 | 366 | int ext4_fname_usr_to_disk(struct inode *inode, |
d5d0e8c7 MH |
367 | const struct qstr *iname, |
368 | struct ext4_str *oname) | |
369 | { | |
370 | int res; | |
b7236e21 | 371 | struct ext4_crypt_info *ci = EXT4_I(inode)->i_crypt_info; |
d5d0e8c7 | 372 | |
d5d0e8c7 MH |
373 | if (iname->len < 3) { |
374 | /*Check for . and .. */ | |
375 | if (iname->name[0] == '.' && | |
376 | iname->name[iname->len-1] == '.') { | |
377 | oname->name[0] = '.'; | |
378 | oname->name[iname->len-1] = '.'; | |
379 | oname->len = iname->len; | |
380 | return oname->len; | |
381 | } | |
382 | } | |
b7236e21 TT |
383 | if (ci) { |
384 | res = ext4_fname_encrypt(inode, iname, oname); | |
d5d0e8c7 MH |
385 | return res; |
386 | } | |
387 | /* Without a proper key, a user is not allowed to modify the filenames | |
388 | * in a directory. Consequently, a user space name cannot be mapped to | |
389 | * a disk-space name */ | |
390 | return -EACCES; | |
391 | } | |
392 | ||
5b643f9c TT |
393 | int ext4_fname_setup_filename(struct inode *dir, const struct qstr *iname, |
394 | int lookup, struct ext4_filename *fname) | |
d5d0e8c7 | 395 | { |
b7236e21 | 396 | struct ext4_crypt_info *ci; |
5b643f9c TT |
397 | int ret = 0, bigname = 0; |
398 | ||
399 | memset(fname, 0, sizeof(struct ext4_filename)); | |
400 | fname->usr_fname = iname; | |
d5d0e8c7 | 401 | |
b7236e21 | 402 | if (!ext4_encrypted_inode(dir) || |
d5d0e8c7 MH |
403 | ((iname->name[0] == '.') && |
404 | ((iname->len == 1) || | |
405 | ((iname->name[1] == '.') && (iname->len == 2))))) { | |
5b643f9c TT |
406 | fname->disk_name.name = (unsigned char *) iname->name; |
407 | fname->disk_name.len = iname->len; | |
82d0d3e7 | 408 | return 0; |
d5d0e8c7 | 409 | } |
c936e1ec | 410 | ret = ext4_get_encryption_info(dir); |
b7236e21 TT |
411 | if (ret) |
412 | return ret; | |
413 | ci = EXT4_I(dir)->i_crypt_info; | |
414 | if (ci) { | |
415 | ret = ext4_fname_crypto_alloc_buffer(dir, iname->len, | |
5b643f9c TT |
416 | &fname->crypto_buf); |
417 | if (ret < 0) | |
82d0d3e7 | 418 | return ret; |
b7236e21 | 419 | ret = ext4_fname_encrypt(dir, iname, &fname->crypto_buf); |
5b643f9c | 420 | if (ret < 0) |
82d0d3e7 | 421 | goto errout; |
5b643f9c TT |
422 | fname->disk_name.name = fname->crypto_buf.name; |
423 | fname->disk_name.len = fname->crypto_buf.len; | |
82d0d3e7 | 424 | return 0; |
5de0b4d0 | 425 | } |
82d0d3e7 TT |
426 | if (!lookup) |
427 | return -EACCES; | |
5de0b4d0 | 428 | |
5b643f9c TT |
429 | /* We don't have the key and we are doing a lookup; decode the |
430 | * user-supplied name | |
431 | */ | |
432 | if (iname->name[0] == '_') | |
433 | bigname = 1; | |
434 | if ((bigname && (iname->len != 33)) || | |
82d0d3e7 TT |
435 | (!bigname && (iname->len > 43))) |
436 | return -ENOENT; | |
437 | ||
5b643f9c | 438 | fname->crypto_buf.name = kmalloc(32, GFP_KERNEL); |
82d0d3e7 TT |
439 | if (fname->crypto_buf.name == NULL) |
440 | return -ENOMEM; | |
5b643f9c TT |
441 | ret = digest_decode(iname->name + bigname, iname->len - bigname, |
442 | fname->crypto_buf.name); | |
443 | if (ret < 0) { | |
444 | ret = -ENOENT; | |
82d0d3e7 | 445 | goto errout; |
5b643f9c TT |
446 | } |
447 | fname->crypto_buf.len = ret; | |
448 | if (bigname) { | |
449 | memcpy(&fname->hinfo.hash, fname->crypto_buf.name, 4); | |
450 | memcpy(&fname->hinfo.minor_hash, fname->crypto_buf.name + 4, 4); | |
451 | } else { | |
452 | fname->disk_name.name = fname->crypto_buf.name; | |
453 | fname->disk_name.len = fname->crypto_buf.len; | |
454 | } | |
82d0d3e7 TT |
455 | return 0; |
456 | errout: | |
457 | kfree(fname->crypto_buf.name); | |
458 | fname->crypto_buf.name = NULL; | |
d5d0e8c7 MH |
459 | return ret; |
460 | } | |
461 | ||
5b643f9c | 462 | void ext4_fname_free_filename(struct ext4_filename *fname) |
d5d0e8c7 | 463 | { |
5b643f9c TT |
464 | kfree(fname->crypto_buf.name); |
465 | fname->crypto_buf.name = NULL; | |
466 | fname->usr_fname = NULL; | |
467 | fname->disk_name.name = NULL; | |
d5d0e8c7 | 468 | } |