diff options
author | Michael Halcrow <mhalcrow@google.com> | 2015-04-12 00:55:06 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2015-04-12 00:55:06 -0400 |
commit | 88bd6ccdcdd638faa11e9746affc21d5f2fe2acf (patch) | |
tree | 1c86a994b5273756f6c90627af2131d8bc616481 /fs/ext4 | |
parent | b30ab0e03407d2aa2d9316cba199c757e4bfc8ad (diff) |
ext4 crypto: add encryption key management facilities
Signed-off-by: Michael Halcrow <mhalcrow@google.com>
Signed-off-by: Ildar Muslukhov <muslukhovi@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/Makefile | 2 | ||||
-rw-r--r-- | fs/ext4/crypto_key.c | 162 | ||||
-rw-r--r-- | fs/ext4/ext4.h | 13 | ||||
-rw-r--r-- | fs/ext4/ext4_crypto.h | 3 |
4 files changed, 179 insertions, 1 deletions
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 1b1c5619523d..4e5af21f1050 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile | |||
@@ -12,4 +12,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o page-io.o \ | |||
12 | 12 | ||
13 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o | 13 | ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o |
14 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o | 14 | ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o |
15 | ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o | 15 | ext4-$(CONFIG_EXT4_FS_ENCRYPTION) += crypto_policy.o crypto.o crypto_key.o |
diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c new file mode 100644 index 000000000000..572bd97f58dd --- /dev/null +++ b/fs/ext4/crypto_key.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * linux/fs/ext4/crypto_key.c | ||
3 | * | ||
4 | * Copyright (C) 2015, Google, Inc. | ||
5 | * | ||
6 | * This contains encryption key functions for ext4 | ||
7 | * | ||
8 | * Written by Michael Halcrow, Ildar Muslukhov, and Uday Savagaonkar, 2015. | ||
9 | */ | ||
10 | |||
11 | #include <keys/encrypted-type.h> | ||
12 | #include <keys/user-type.h> | ||
13 | #include <linux/random.h> | ||
14 | #include <linux/scatterlist.h> | ||
15 | #include <uapi/linux/keyctl.h> | ||
16 | |||
17 | #include "ext4.h" | ||
18 | #include "xattr.h" | ||
19 | |||
20 | static void derive_crypt_complete(struct crypto_async_request *req, int rc) | ||
21 | { | ||
22 | struct ext4_completion_result *ecr = req->data; | ||
23 | |||
24 | if (rc == -EINPROGRESS) | ||
25 | return; | ||
26 | |||
27 | ecr->res = rc; | ||
28 | complete(&ecr->completion); | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * ext4_derive_key_aes() - Derive a key using AES-128-ECB | ||
33 | * @deriving_key: Encryption key used for derivatio. | ||
34 | * @source_key: Source key to which to apply derivation. | ||
35 | * @derived_key: Derived key. | ||
36 | * | ||
37 | * Return: Zero on success; non-zero otherwise. | ||
38 | */ | ||
39 | static int ext4_derive_key_aes(char deriving_key[EXT4_AES_128_ECB_KEY_SIZE], | ||
40 | char source_key[EXT4_AES_256_XTS_KEY_SIZE], | ||
41 | char derived_key[EXT4_AES_256_XTS_KEY_SIZE]) | ||
42 | { | ||
43 | int res = 0; | ||
44 | struct ablkcipher_request *req = NULL; | ||
45 | DECLARE_EXT4_COMPLETION_RESULT(ecr); | ||
46 | struct scatterlist src_sg, dst_sg; | ||
47 | struct crypto_ablkcipher *tfm = crypto_alloc_ablkcipher("ecb(aes)", 0, | ||
48 | 0); | ||
49 | |||
50 | if (IS_ERR(tfm)) { | ||
51 | res = PTR_ERR(tfm); | ||
52 | tfm = NULL; | ||
53 | goto out; | ||
54 | } | ||
55 | crypto_ablkcipher_set_flags(tfm, CRYPTO_TFM_REQ_WEAK_KEY); | ||
56 | req = ablkcipher_request_alloc(tfm, GFP_NOFS); | ||
57 | if (!req) { | ||
58 | res = -ENOMEM; | ||
59 | goto out; | ||
60 | } | ||
61 | ablkcipher_request_set_callback(req, | ||
62 | CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP, | ||
63 | derive_crypt_complete, &ecr); | ||
64 | res = crypto_ablkcipher_setkey(tfm, deriving_key, | ||
65 | EXT4_AES_128_ECB_KEY_SIZE); | ||
66 | if (res < 0) | ||
67 | goto out; | ||
68 | sg_init_one(&src_sg, source_key, EXT4_AES_256_XTS_KEY_SIZE); | ||
69 | sg_init_one(&dst_sg, derived_key, EXT4_AES_256_XTS_KEY_SIZE); | ||
70 | ablkcipher_request_set_crypt(req, &src_sg, &dst_sg, | ||
71 | EXT4_AES_256_XTS_KEY_SIZE, NULL); | ||
72 | res = crypto_ablkcipher_encrypt(req); | ||
73 | if (res == -EINPROGRESS || res == -EBUSY) { | ||
74 | BUG_ON(req->base.data != &ecr); | ||
75 | wait_for_completion(&ecr.completion); | ||
76 | res = ecr.res; | ||
77 | } | ||
78 | |||
79 | out: | ||
80 | if (req) | ||
81 | ablkcipher_request_free(req); | ||
82 | if (tfm) | ||
83 | crypto_free_ablkcipher(tfm); | ||
84 | return res; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * ext4_generate_encryption_key() - generates an encryption key | ||
89 | * @inode: The inode to generate the encryption key for. | ||
90 | */ | ||
91 | int ext4_generate_encryption_key(struct inode *inode) | ||
92 | { | ||
93 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
94 | struct ext4_encryption_key *crypt_key = &ei->i_encryption_key; | ||
95 | char full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE + | ||
96 | (EXT4_KEY_DESCRIPTOR_SIZE * 2) + 1]; | ||
97 | struct key *keyring_key = NULL; | ||
98 | struct ext4_encryption_key *master_key; | ||
99 | struct ext4_encryption_context ctx; | ||
100 | struct user_key_payload *ukp; | ||
101 | int res = ext4_xattr_get(inode, EXT4_XATTR_INDEX_ENCRYPTION, | ||
102 | EXT4_XATTR_NAME_ENCRYPTION_CONTEXT, | ||
103 | &ctx, sizeof(ctx)); | ||
104 | |||
105 | if (res != sizeof(ctx)) { | ||
106 | if (res > 0) | ||
107 | res = -EINVAL; | ||
108 | goto out; | ||
109 | } | ||
110 | res = 0; | ||
111 | |||
112 | memcpy(full_key_descriptor, EXT4_KEY_DESC_PREFIX, | ||
113 | EXT4_KEY_DESC_PREFIX_SIZE); | ||
114 | sprintf(full_key_descriptor + EXT4_KEY_DESC_PREFIX_SIZE, | ||
115 | "%*phN", EXT4_KEY_DESCRIPTOR_SIZE, | ||
116 | ctx.master_key_descriptor); | ||
117 | full_key_descriptor[EXT4_KEY_DESC_PREFIX_SIZE + | ||
118 | (2 * EXT4_KEY_DESCRIPTOR_SIZE)] = '\0'; | ||
119 | keyring_key = request_key(&key_type_logon, full_key_descriptor, NULL); | ||
120 | if (IS_ERR(keyring_key)) { | ||
121 | res = PTR_ERR(keyring_key); | ||
122 | keyring_key = NULL; | ||
123 | goto out; | ||
124 | } | ||
125 | BUG_ON(keyring_key->type != &key_type_logon); | ||
126 | ukp = ((struct user_key_payload *)keyring_key->payload.data); | ||
127 | if (ukp->datalen != sizeof(struct ext4_encryption_key)) { | ||
128 | res = -EINVAL; | ||
129 | goto out; | ||
130 | } | ||
131 | master_key = (struct ext4_encryption_key *)ukp->data; | ||
132 | |||
133 | if (S_ISREG(inode->i_mode)) | ||
134 | crypt_key->mode = ctx.contents_encryption_mode; | ||
135 | else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) | ||
136 | crypt_key->mode = ctx.filenames_encryption_mode; | ||
137 | else { | ||
138 | printk(KERN_ERR "ext4 crypto: Unsupported inode type.\n"); | ||
139 | BUG(); | ||
140 | } | ||
141 | crypt_key->size = ext4_encryption_key_size(crypt_key->mode); | ||
142 | BUG_ON(!crypt_key->size); | ||
143 | BUILD_BUG_ON(EXT4_AES_128_ECB_KEY_SIZE != | ||
144 | EXT4_KEY_DERIVATION_NONCE_SIZE); | ||
145 | BUG_ON(master_key->size != EXT4_AES_256_XTS_KEY_SIZE); | ||
146 | BUG_ON(crypt_key->size < EXT4_AES_256_CBC_KEY_SIZE); | ||
147 | res = ext4_derive_key_aes(ctx.nonce, master_key->raw, crypt_key->raw); | ||
148 | out: | ||
149 | if (keyring_key) | ||
150 | key_put(keyring_key); | ||
151 | if (res < 0) | ||
152 | crypt_key->mode = EXT4_ENCRYPTION_MODE_INVALID; | ||
153 | return res; | ||
154 | } | ||
155 | |||
156 | int ext4_has_encryption_key(struct inode *inode) | ||
157 | { | ||
158 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
159 | struct ext4_encryption_key *crypt_key = &ei->i_encryption_key; | ||
160 | |||
161 | return (crypt_key->mode != EXT4_ENCRYPTION_MODE_INVALID); | ||
162 | } | ||
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 71619ef72225..99a2d67f65b7 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2078,6 +2078,19 @@ static inline int ext4_sb_has_crypto(struct super_block *sb) | |||
2078 | } | 2078 | } |
2079 | #endif | 2079 | #endif |
2080 | 2080 | ||
2081 | /* crypto_key.c */ | ||
2082 | int ext4_generate_encryption_key(struct inode *inode); | ||
2083 | |||
2084 | #ifdef CONFIG_EXT4_FS_ENCRYPTION | ||
2085 | int ext4_has_encryption_key(struct inode *inode); | ||
2086 | #else | ||
2087 | static inline int ext4_has_encryption_key(struct inode *inode) | ||
2088 | { | ||
2089 | return 0; | ||
2090 | } | ||
2091 | #endif | ||
2092 | |||
2093 | |||
2081 | /* dir.c */ | 2094 | /* dir.c */ |
2082 | extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, | 2095 | extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, |
2083 | struct file *, | 2096 | struct file *, |
diff --git a/fs/ext4/ext4_crypto.h b/fs/ext4/ext4_crypto.h index 9d5d2e56cc46..6a7c0c06b2be 100644 --- a/fs/ext4/ext4_crypto.h +++ b/fs/ext4/ext4_crypto.h | |||
@@ -55,6 +55,9 @@ struct ext4_encryption_context { | |||
55 | #define EXT4_AES_256_XTS_KEY_SIZE 64 | 55 | #define EXT4_AES_256_XTS_KEY_SIZE 64 |
56 | #define EXT4_MAX_KEY_SIZE 64 | 56 | #define EXT4_MAX_KEY_SIZE 64 |
57 | 57 | ||
58 | #define EXT4_KEY_DESC_PREFIX "ext4:" | ||
59 | #define EXT4_KEY_DESC_PREFIX_SIZE 5 | ||
60 | |||
58 | struct ext4_encryption_key { | 61 | struct ext4_encryption_key { |
59 | uint32_t mode; | 62 | uint32_t mode; |
60 | char raw[EXT4_MAX_KEY_SIZE]; | 63 | char raw[EXT4_MAX_KEY_SIZE]; |