]>
Commit | Line | Data |
---|---|---|
0b81d077 JK |
1 | /* |
2 | * Encryption policy functions for per-file encryption support. | |
3 | * | |
4 | * Copyright (C) 2015, Google, Inc. | |
5 | * Copyright (C) 2015, Motorola Mobility. | |
6 | * | |
7 | * Written by Michael Halcrow, 2015. | |
8 | * Modified by Jaegeuk Kim, 2015. | |
9 | */ | |
10 | ||
11 | #include <linux/random.h> | |
12 | #include <linux/string.h> | |
ba63f23d | 13 | #include <linux/mount.h> |
cc4e0df0 | 14 | #include "fscrypt_private.h" |
0b81d077 | 15 | |
0b81d077 | 16 | /* |
efee590e | 17 | * check whether an encryption policy is consistent with an encryption context |
0b81d077 | 18 | */ |
efee590e EB |
19 | static bool is_encryption_context_consistent_with_policy( |
20 | const struct fscrypt_context *ctx, | |
0b81d077 JK |
21 | const struct fscrypt_policy *policy) |
22 | { | |
efee590e EB |
23 | return memcmp(ctx->master_key_descriptor, policy->master_key_descriptor, |
24 | FS_KEY_DESCRIPTOR_SIZE) == 0 && | |
25 | (ctx->flags == policy->flags) && | |
26 | (ctx->contents_encryption_mode == | |
27 | policy->contents_encryption_mode) && | |
28 | (ctx->filenames_encryption_mode == | |
29 | policy->filenames_encryption_mode); | |
0b81d077 JK |
30 | } |
31 | ||
32 | static int create_encryption_context_from_policy(struct inode *inode, | |
33 | const struct fscrypt_policy *policy) | |
34 | { | |
35 | struct fscrypt_context ctx; | |
0b81d077 JK |
36 | |
37 | if (!inode->i_sb->s_cop->set_context) | |
38 | return -EOPNOTSUPP; | |
39 | ||
0b81d077 JK |
40 | ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; |
41 | memcpy(ctx.master_key_descriptor, policy->master_key_descriptor, | |
42 | FS_KEY_DESCRIPTOR_SIZE); | |
43 | ||
44 | if (!fscrypt_valid_contents_enc_mode( | |
868e1bc6 | 45 | policy->contents_encryption_mode)) |
0b81d077 | 46 | return -EINVAL; |
0b81d077 JK |
47 | |
48 | if (!fscrypt_valid_filenames_enc_mode( | |
868e1bc6 | 49 | policy->filenames_encryption_mode)) |
0b81d077 | 50 | return -EINVAL; |
0b81d077 JK |
51 | |
52 | if (policy->flags & ~FS_POLICY_FLAGS_VALID) | |
53 | return -EINVAL; | |
54 | ||
55 | ctx.contents_encryption_mode = policy->contents_encryption_mode; | |
56 | ctx.filenames_encryption_mode = policy->filenames_encryption_mode; | |
57 | ctx.flags = policy->flags; | |
58 | BUILD_BUG_ON(sizeof(ctx.nonce) != FS_KEY_DERIVATION_NONCE_SIZE); | |
59 | get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); | |
60 | ||
61 | return inode->i_sb->s_cop->set_context(inode, &ctx, sizeof(ctx), NULL); | |
62 | } | |
63 | ||
db717d8e | 64 | int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) |
0b81d077 | 65 | { |
db717d8e | 66 | struct fscrypt_policy policy; |
ba63f23d EB |
67 | struct inode *inode = file_inode(filp); |
68 | int ret; | |
efee590e | 69 | struct fscrypt_context ctx; |
ba63f23d | 70 | |
db717d8e EB |
71 | if (copy_from_user(&policy, arg, sizeof(policy))) |
72 | return -EFAULT; | |
73 | ||
163ae1c6 EB |
74 | if (!inode_owner_or_capable(inode)) |
75 | return -EACCES; | |
76 | ||
db717d8e | 77 | if (policy.version != 0) |
0b81d077 JK |
78 | return -EINVAL; |
79 | ||
ba63f23d EB |
80 | ret = mnt_want_write_file(filp); |
81 | if (ret) | |
82 | return ret; | |
83 | ||
8906a822 EB |
84 | inode_lock(inode); |
85 | ||
efee590e EB |
86 | ret = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); |
87 | if (ret == -ENODATA) { | |
002ced4b | 88 | if (!S_ISDIR(inode->i_mode)) |
dffd0cfa | 89 | ret = -ENOTDIR; |
ba63f23d EB |
90 | else if (!inode->i_sb->s_cop->empty_dir) |
91 | ret = -EOPNOTSUPP; | |
92 | else if (!inode->i_sb->s_cop->empty_dir(inode)) | |
93 | ret = -ENOTEMPTY; | |
94 | else | |
95 | ret = create_encryption_context_from_policy(inode, | |
db717d8e | 96 | &policy); |
efee590e EB |
97 | } else if (ret == sizeof(ctx) && |
98 | is_encryption_context_consistent_with_policy(&ctx, | |
99 | &policy)) { | |
100 | /* The file already uses the same encryption policy. */ | |
101 | ret = 0; | |
102 | } else if (ret >= 0 || ret == -ERANGE) { | |
103 | /* The file already uses a different encryption policy. */ | |
8488cd96 | 104 | ret = -EEXIST; |
0b81d077 JK |
105 | } |
106 | ||
8906a822 EB |
107 | inode_unlock(inode); |
108 | ||
ba63f23d EB |
109 | mnt_drop_write_file(filp); |
110 | return ret; | |
0b81d077 | 111 | } |
db717d8e | 112 | EXPORT_SYMBOL(fscrypt_ioctl_set_policy); |
0b81d077 | 113 | |
db717d8e | 114 | int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) |
0b81d077 | 115 | { |
db717d8e | 116 | struct inode *inode = file_inode(filp); |
0b81d077 | 117 | struct fscrypt_context ctx; |
db717d8e | 118 | struct fscrypt_policy policy; |
0b81d077 JK |
119 | int res; |
120 | ||
121 | if (!inode->i_sb->s_cop->get_context || | |
122 | !inode->i_sb->s_cop->is_encrypted(inode)) | |
123 | return -ENODATA; | |
124 | ||
125 | res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); | |
efee590e EB |
126 | if (res < 0 && res != -ERANGE) |
127 | return res; | |
0b81d077 | 128 | if (res != sizeof(ctx)) |
efee590e | 129 | return -EINVAL; |
0b81d077 JK |
130 | if (ctx.format != FS_ENCRYPTION_CONTEXT_FORMAT_V1) |
131 | return -EINVAL; | |
132 | ||
db717d8e EB |
133 | policy.version = 0; |
134 | policy.contents_encryption_mode = ctx.contents_encryption_mode; | |
135 | policy.filenames_encryption_mode = ctx.filenames_encryption_mode; | |
136 | policy.flags = ctx.flags; | |
137 | memcpy(policy.master_key_descriptor, ctx.master_key_descriptor, | |
0b81d077 | 138 | FS_KEY_DESCRIPTOR_SIZE); |
db717d8e EB |
139 | |
140 | if (copy_to_user(arg, &policy, sizeof(policy))) | |
141 | return -EFAULT; | |
0b81d077 JK |
142 | return 0; |
143 | } | |
db717d8e | 144 | EXPORT_SYMBOL(fscrypt_ioctl_get_policy); |
0b81d077 JK |
145 | |
146 | int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) | |
147 | { | |
148 | struct fscrypt_info *parent_ci, *child_ci; | |
149 | int res; | |
150 | ||
151 | if ((parent == NULL) || (child == NULL)) { | |
152 | printk(KERN_ERR "parent %p child %p\n", parent, child); | |
153 | BUG_ON(1); | |
154 | } | |
155 | ||
42d97eb0 EB |
156 | /* No restrictions on file types which are never encrypted */ |
157 | if (!S_ISREG(child->i_mode) && !S_ISDIR(child->i_mode) && | |
158 | !S_ISLNK(child->i_mode)) | |
159 | return 1; | |
160 | ||
0b81d077 JK |
161 | /* no restrictions if the parent directory is not encrypted */ |
162 | if (!parent->i_sb->s_cop->is_encrypted(parent)) | |
163 | return 1; | |
164 | /* if the child directory is not encrypted, this is always a problem */ | |
165 | if (!parent->i_sb->s_cop->is_encrypted(child)) | |
166 | return 0; | |
167 | res = fscrypt_get_encryption_info(parent); | |
168 | if (res) | |
169 | return 0; | |
170 | res = fscrypt_get_encryption_info(child); | |
171 | if (res) | |
172 | return 0; | |
173 | parent_ci = parent->i_crypt_info; | |
174 | child_ci = child->i_crypt_info; | |
175 | if (!parent_ci && !child_ci) | |
176 | return 1; | |
177 | if (!parent_ci || !child_ci) | |
178 | return 0; | |
179 | ||
180 | return (memcmp(parent_ci->ci_master_key, | |
181 | child_ci->ci_master_key, | |
182 | FS_KEY_DESCRIPTOR_SIZE) == 0 && | |
183 | (parent_ci->ci_data_mode == child_ci->ci_data_mode) && | |
184 | (parent_ci->ci_filename_mode == child_ci->ci_filename_mode) && | |
185 | (parent_ci->ci_flags == child_ci->ci_flags)); | |
186 | } | |
187 | EXPORT_SYMBOL(fscrypt_has_permitted_context); | |
188 | ||
189 | /** | |
190 | * fscrypt_inherit_context() - Sets a child context from its parent | |
191 | * @parent: Parent inode from which the context is inherited. | |
192 | * @child: Child inode that inherits the context from @parent. | |
193 | * @fs_data: private data given by FS. | |
5bbdcbbb | 194 | * @preload: preload child i_crypt_info if true |
0b81d077 | 195 | * |
5bbdcbbb | 196 | * Return: 0 on success, -errno on failure |
0b81d077 JK |
197 | */ |
198 | int fscrypt_inherit_context(struct inode *parent, struct inode *child, | |
199 | void *fs_data, bool preload) | |
200 | { | |
201 | struct fscrypt_context ctx; | |
202 | struct fscrypt_info *ci; | |
203 | int res; | |
204 | ||
205 | if (!parent->i_sb->s_cop->set_context) | |
206 | return -EOPNOTSUPP; | |
207 | ||
208 | res = fscrypt_get_encryption_info(parent); | |
209 | if (res < 0) | |
210 | return res; | |
211 | ||
212 | ci = parent->i_crypt_info; | |
213 | if (ci == NULL) | |
214 | return -ENOKEY; | |
215 | ||
216 | ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1; | |
5bbdcbbb TT |
217 | ctx.contents_encryption_mode = ci->ci_data_mode; |
218 | ctx.filenames_encryption_mode = ci->ci_filename_mode; | |
219 | ctx.flags = ci->ci_flags; | |
220 | memcpy(ctx.master_key_descriptor, ci->ci_master_key, | |
221 | FS_KEY_DESCRIPTOR_SIZE); | |
0b81d077 JK |
222 | get_random_bytes(ctx.nonce, FS_KEY_DERIVATION_NONCE_SIZE); |
223 | res = parent->i_sb->s_cop->set_context(child, &ctx, | |
224 | sizeof(ctx), fs_data); | |
225 | if (res) | |
226 | return res; | |
227 | return preload ? fscrypt_get_encryption_info(child): 0; | |
228 | } | |
229 | EXPORT_SYMBOL(fscrypt_inherit_context); |