]>
Commit | Line | Data |
---|---|---|
2fe5d6de MZ |
1 | /* |
2 | * Copyright (C) 2011 IBM Corporation | |
3 | * | |
4 | * Author: | |
5 | * Mimi Zohar <zohar@us.ibm.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, version 2 of the License. | |
10 | */ | |
11 | #include <linux/module.h> | |
12 | #include <linux/file.h> | |
13 | #include <linux/fs.h> | |
14 | #include <linux/xattr.h> | |
15 | #include <linux/magic.h> | |
16 | #include <linux/ima.h> | |
17 | #include <linux/evm.h> | |
18 | ||
19 | #include "ima.h" | |
20 | ||
21 | static int __init default_appraise_setup(char *str) | |
22 | { | |
23 | if (strncmp(str, "off", 3) == 0) | |
24 | ima_appraise = 0; | |
25 | else if (strncmp(str, "fix", 3) == 0) | |
26 | ima_appraise = IMA_APPRAISE_FIX; | |
27 | return 1; | |
28 | } | |
29 | ||
30 | __setup("ima_appraise=", default_appraise_setup); | |
31 | ||
32 | /* | |
33 | * ima_must_appraise - set appraise flag | |
34 | * | |
35 | * Return 1 to appraise | |
36 | */ | |
d26e1936 | 37 | int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func) |
2fe5d6de | 38 | { |
07f6a794 MZ |
39 | if (!ima_appraise) |
40 | return 0; | |
41 | ||
42 | return ima_match_policy(inode, func, mask, IMA_APPRAISE); | |
2fe5d6de MZ |
43 | } |
44 | ||
def3e8b9 | 45 | static int ima_fix_xattr(struct dentry *dentry, |
c7c8bb23 | 46 | struct integrity_iint_cache *iint) |
2fe5d6de | 47 | { |
c7c8bb23 | 48 | iint->ima_hash.type = IMA_XATTR_DIGEST; |
def3e8b9 | 49 | return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, |
c7c8bb23 DK |
50 | &iint->ima_hash.type, |
51 | 1 + iint->ima_hash.length, 0); | |
2fe5d6de MZ |
52 | } |
53 | ||
d79d72e0 MZ |
54 | /* Return specific func appraised cached result */ |
55 | enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, | |
56 | int func) | |
57 | { | |
089bc8e9 | 58 | switch (func) { |
d79d72e0 MZ |
59 | case MMAP_CHECK: |
60 | return iint->ima_mmap_status; | |
61 | case BPRM_CHECK: | |
62 | return iint->ima_bprm_status; | |
63 | case MODULE_CHECK: | |
64 | return iint->ima_module_status; | |
65 | case FILE_CHECK: | |
66 | default: | |
67 | return iint->ima_file_status; | |
68 | } | |
69 | } | |
70 | ||
71 | static void ima_set_cache_status(struct integrity_iint_cache *iint, | |
72 | int func, enum integrity_status status) | |
73 | { | |
089bc8e9 | 74 | switch (func) { |
d79d72e0 MZ |
75 | case MMAP_CHECK: |
76 | iint->ima_mmap_status = status; | |
77 | break; | |
78 | case BPRM_CHECK: | |
79 | iint->ima_bprm_status = status; | |
80 | break; | |
81 | case MODULE_CHECK: | |
82 | iint->ima_module_status = status; | |
83 | break; | |
84 | case FILE_CHECK: | |
85 | default: | |
86 | iint->ima_file_status = status; | |
87 | break; | |
88 | } | |
89 | } | |
90 | ||
91 | static void ima_cache_flags(struct integrity_iint_cache *iint, int func) | |
92 | { | |
089bc8e9 | 93 | switch (func) { |
d79d72e0 MZ |
94 | case MMAP_CHECK: |
95 | iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED); | |
96 | break; | |
97 | case BPRM_CHECK: | |
98 | iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED); | |
99 | break; | |
100 | case MODULE_CHECK: | |
101 | iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); | |
102 | break; | |
103 | case FILE_CHECK: | |
104 | default: | |
105 | iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); | |
106 | break; | |
107 | } | |
108 | } | |
109 | ||
d3634d0f DK |
110 | void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, |
111 | struct ima_digest_data *hash) | |
112 | { | |
113 | struct signature_v2_hdr *sig; | |
114 | ||
115 | if (!xattr_value || xattr_len < 0 || xattr_len <= 1 + sizeof(*sig)) | |
116 | return; | |
117 | ||
118 | sig = (typeof(sig)) xattr_value->digest; | |
119 | ||
120 | if (xattr_value->type != EVM_IMA_XATTR_DIGSIG || sig->version != 2) | |
121 | return; | |
122 | ||
123 | hash->algo = sig->hash_algo; | |
124 | } | |
125 | ||
126 | int ima_read_xattr(struct dentry *dentry, | |
127 | struct evm_ima_xattr_data **xattr_value) | |
128 | { | |
129 | struct inode *inode = dentry->d_inode; | |
130 | ||
131 | if (!inode->i_op->getxattr) | |
132 | return 0; | |
133 | ||
134 | return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, | |
135 | 0, GFP_NOFS); | |
136 | } | |
137 | ||
2fe5d6de MZ |
138 | /* |
139 | * ima_appraise_measurement - appraise file measurement | |
140 | * | |
141 | * Call evm_verifyxattr() to verify the integrity of 'security.ima'. | |
142 | * Assuming success, compare the xattr hash with the collected measurement. | |
143 | * | |
144 | * Return 0 on success, error code otherwise | |
145 | */ | |
d79d72e0 | 146 | int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, |
d3634d0f DK |
147 | struct file *file, const unsigned char *filename, |
148 | struct evm_ima_xattr_data *xattr_value, | |
149 | int xattr_len) | |
2fe5d6de MZ |
150 | { |
151 | struct dentry *dentry = file->f_dentry; | |
152 | struct inode *inode = dentry->d_inode; | |
2fe5d6de MZ |
153 | enum integrity_status status = INTEGRITY_UNKNOWN; |
154 | const char *op = "appraise_data"; | |
155 | char *cause = "unknown"; | |
d3634d0f | 156 | int rc = xattr_len; |
2fe5d6de MZ |
157 | |
158 | if (!ima_appraise) | |
159 | return 0; | |
160 | if (!inode->i_op->getxattr) | |
161 | return INTEGRITY_UNKNOWN; | |
162 | ||
2fe5d6de MZ |
163 | if (rc <= 0) { |
164 | if (rc && rc != -ENODATA) | |
165 | goto out; | |
166 | ||
167 | cause = "missing-hash"; | |
168 | status = | |
169 | (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; | |
170 | goto out; | |
171 | } | |
172 | ||
8606404f | 173 | status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); |
2fe5d6de MZ |
174 | if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { |
175 | if ((status == INTEGRITY_NOLABEL) | |
176 | || (status == INTEGRITY_NOXATTRS)) | |
177 | cause = "missing-HMAC"; | |
178 | else if (status == INTEGRITY_FAIL) | |
179 | cause = "invalid-HMAC"; | |
180 | goto out; | |
181 | } | |
8606404f DK |
182 | switch (xattr_value->type) { |
183 | case IMA_XATTR_DIGEST: | |
0e5a247c DK |
184 | if (iint->flags & IMA_DIGSIG_REQUIRED) { |
185 | cause = "IMA signature required"; | |
186 | status = INTEGRITY_FAIL; | |
187 | break; | |
188 | } | |
d3634d0f DK |
189 | if (xattr_len - 1 >= iint->ima_hash.length) |
190 | /* xattr length may be longer. md5 hash in previous | |
191 | version occupied 20 bytes in xattr, instead of 16 | |
192 | */ | |
c7c8bb23 DK |
193 | rc = memcmp(xattr_value->digest, |
194 | iint->ima_hash.digest, | |
195 | iint->ima_hash.length); | |
196 | else | |
197 | rc = -EINVAL; | |
8606404f DK |
198 | if (rc) { |
199 | cause = "invalid-hash"; | |
200 | status = INTEGRITY_FAIL; | |
8606404f DK |
201 | break; |
202 | } | |
203 | status = INTEGRITY_PASS; | |
204 | break; | |
205 | case EVM_IMA_XATTR_DIGSIG: | |
206 | iint->flags |= IMA_DIGSIG; | |
207 | rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, | |
b1aaab22 | 208 | (const char *)xattr_value, rc, |
c7c8bb23 DK |
209 | iint->ima_hash.digest, |
210 | iint->ima_hash.length); | |
8606404f DK |
211 | if (rc == -EOPNOTSUPP) { |
212 | status = INTEGRITY_UNKNOWN; | |
213 | } else if (rc) { | |
214 | cause = "invalid-signature"; | |
215 | status = INTEGRITY_FAIL; | |
216 | } else { | |
217 | status = INTEGRITY_PASS; | |
218 | } | |
219 | break; | |
220 | default: | |
221 | status = INTEGRITY_UNKNOWN; | |
222 | cause = "unknown-ima-data"; | |
223 | break; | |
2fe5d6de | 224 | } |
8606404f | 225 | |
2fe5d6de MZ |
226 | out: |
227 | if (status != INTEGRITY_PASS) { | |
8606404f DK |
228 | if ((ima_appraise & IMA_APPRAISE_FIX) && |
229 | (!xattr_value || | |
230 | xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { | |
def3e8b9 DK |
231 | if (!ima_fix_xattr(dentry, iint)) |
232 | status = INTEGRITY_PASS; | |
2fe5d6de MZ |
233 | } |
234 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, | |
235 | op, cause, rc, 0); | |
8606404f | 236 | } else { |
d79d72e0 | 237 | ima_cache_flags(iint, func); |
2fe5d6de | 238 | } |
d79d72e0 | 239 | ima_set_cache_status(iint, func, status); |
2fe5d6de MZ |
240 | return status; |
241 | } | |
242 | ||
243 | /* | |
244 | * ima_update_xattr - update 'security.ima' hash value | |
245 | */ | |
246 | void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) | |
247 | { | |
248 | struct dentry *dentry = file->f_dentry; | |
249 | int rc = 0; | |
250 | ||
8606404f DK |
251 | /* do not collect and update hash for digital signatures */ |
252 | if (iint->flags & IMA_DIGSIG) | |
253 | return; | |
254 | ||
d3634d0f | 255 | rc = ima_collect_measurement(iint, file, NULL, NULL); |
2fe5d6de MZ |
256 | if (rc < 0) |
257 | return; | |
8606404f | 258 | |
2fe5d6de MZ |
259 | ima_fix_xattr(dentry, iint); |
260 | } | |
261 | ||
262 | /** | |
263 | * ima_inode_post_setattr - reflect file metadata changes | |
264 | * @dentry: pointer to the affected dentry | |
265 | * | |
266 | * Changes to a dentry's metadata might result in needing to appraise. | |
267 | * | |
268 | * This function is called from notify_change(), which expects the caller | |
269 | * to lock the inode's i_mutex. | |
270 | */ | |
271 | void ima_inode_post_setattr(struct dentry *dentry) | |
272 | { | |
273 | struct inode *inode = dentry->d_inode; | |
274 | struct integrity_iint_cache *iint; | |
275 | int must_appraise, rc; | |
276 | ||
277 | if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) | |
278 | || !inode->i_op->removexattr) | |
279 | return; | |
280 | ||
281 | must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); | |
282 | iint = integrity_iint_find(inode); | |
283 | if (iint) { | |
d79d72e0 MZ |
284 | iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | |
285 | IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | | |
286 | IMA_ACTION_FLAGS); | |
2fe5d6de MZ |
287 | if (must_appraise) |
288 | iint->flags |= IMA_APPRAISE; | |
2fe5d6de MZ |
289 | } |
290 | if (!must_appraise) | |
291 | rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); | |
292 | return; | |
293 | } | |
42c63330 MZ |
294 | |
295 | /* | |
296 | * ima_protect_xattr - protect 'security.ima' | |
297 | * | |
298 | * Ensure that not just anyone can modify or remove 'security.ima'. | |
299 | */ | |
300 | static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, | |
301 | const void *xattr_value, size_t xattr_value_len) | |
302 | { | |
303 | if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) { | |
304 | if (!capable(CAP_SYS_ADMIN)) | |
305 | return -EPERM; | |
306 | return 1; | |
307 | } | |
308 | return 0; | |
309 | } | |
310 | ||
311 | static void ima_reset_appraise_flags(struct inode *inode) | |
312 | { | |
313 | struct integrity_iint_cache *iint; | |
314 | ||
315 | if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) | |
316 | return; | |
317 | ||
318 | iint = integrity_iint_find(inode); | |
319 | if (!iint) | |
320 | return; | |
321 | ||
45e2472e | 322 | iint->flags &= ~IMA_DONE_MASK; |
42c63330 MZ |
323 | return; |
324 | } | |
325 | ||
326 | int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, | |
327 | const void *xattr_value, size_t xattr_value_len) | |
328 | { | |
329 | int result; | |
330 | ||
331 | result = ima_protect_xattr(dentry, xattr_name, xattr_value, | |
332 | xattr_value_len); | |
333 | if (result == 1) { | |
334 | ima_reset_appraise_flags(dentry->d_inode); | |
335 | result = 0; | |
336 | } | |
337 | return result; | |
338 | } | |
339 | ||
340 | int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) | |
341 | { | |
342 | int result; | |
343 | ||
344 | result = ima_protect_xattr(dentry, xattr_name, NULL, 0); | |
345 | if (result == 1) { | |
346 | ima_reset_appraise_flags(dentry->d_inode); | |
347 | result = 0; | |
348 | } | |
349 | return result; | |
350 | } |