]>
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> | |
3ea7a560 | 18 | #include <crypto/hash_info.h> |
2fe5d6de MZ |
19 | |
20 | #include "ima.h" | |
21 | ||
22 | static int __init default_appraise_setup(char *str) | |
23 | { | |
24 | if (strncmp(str, "off", 3) == 0) | |
25 | ima_appraise = 0; | |
2faa6ef3 DK |
26 | else if (strncmp(str, "log", 3) == 0) |
27 | ima_appraise = IMA_APPRAISE_LOG; | |
2fe5d6de MZ |
28 | else if (strncmp(str, "fix", 3) == 0) |
29 | ima_appraise = IMA_APPRAISE_FIX; | |
30 | return 1; | |
31 | } | |
32 | ||
33 | __setup("ima_appraise=", default_appraise_setup); | |
34 | ||
35 | /* | |
36 | * ima_must_appraise - set appraise flag | |
37 | * | |
38 | * Return 1 to appraise | |
39 | */ | |
d26e1936 | 40 | int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func) |
2fe5d6de | 41 | { |
07f6a794 MZ |
42 | if (!ima_appraise) |
43 | return 0; | |
44 | ||
45 | return ima_match_policy(inode, func, mask, IMA_APPRAISE); | |
2fe5d6de MZ |
46 | } |
47 | ||
def3e8b9 | 48 | static int ima_fix_xattr(struct dentry *dentry, |
c7c8bb23 | 49 | struct integrity_iint_cache *iint) |
2fe5d6de | 50 | { |
3ea7a560 DK |
51 | int rc, offset; |
52 | u8 algo = iint->ima_hash->algo; | |
53 | ||
54 | if (algo <= HASH_ALGO_SHA1) { | |
55 | offset = 1; | |
56 | iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST; | |
57 | } else { | |
58 | offset = 0; | |
59 | iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; | |
60 | iint->ima_hash->xattr.ng.algo = algo; | |
61 | } | |
62 | rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, | |
63 | &iint->ima_hash->xattr.data[offset], | |
64 | (sizeof(iint->ima_hash->xattr) - offset) + | |
65 | iint->ima_hash->length, 0); | |
66 | return rc; | |
2fe5d6de MZ |
67 | } |
68 | ||
d79d72e0 MZ |
69 | /* Return specific func appraised cached result */ |
70 | enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, | |
71 | int func) | |
72 | { | |
089bc8e9 | 73 | switch (func) { |
d79d72e0 MZ |
74 | case MMAP_CHECK: |
75 | return iint->ima_mmap_status; | |
76 | case BPRM_CHECK: | |
77 | return iint->ima_bprm_status; | |
78 | case MODULE_CHECK: | |
79 | return iint->ima_module_status; | |
5a9196d7 MZ |
80 | case FIRMWARE_CHECK: |
81 | return iint->ima_firmware_status; | |
d79d72e0 MZ |
82 | case FILE_CHECK: |
83 | default: | |
84 | return iint->ima_file_status; | |
85 | } | |
86 | } | |
87 | ||
88 | static void ima_set_cache_status(struct integrity_iint_cache *iint, | |
89 | int func, enum integrity_status status) | |
90 | { | |
089bc8e9 | 91 | switch (func) { |
d79d72e0 MZ |
92 | case MMAP_CHECK: |
93 | iint->ima_mmap_status = status; | |
94 | break; | |
95 | case BPRM_CHECK: | |
96 | iint->ima_bprm_status = status; | |
97 | break; | |
98 | case MODULE_CHECK: | |
99 | iint->ima_module_status = status; | |
100 | break; | |
5a9196d7 MZ |
101 | case FIRMWARE_CHECK: |
102 | iint->ima_firmware_status = status; | |
103 | break; | |
d79d72e0 MZ |
104 | case FILE_CHECK: |
105 | default: | |
106 | iint->ima_file_status = status; | |
107 | break; | |
108 | } | |
109 | } | |
110 | ||
111 | static void ima_cache_flags(struct integrity_iint_cache *iint, int func) | |
112 | { | |
089bc8e9 | 113 | switch (func) { |
d79d72e0 MZ |
114 | case MMAP_CHECK: |
115 | iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED); | |
116 | break; | |
117 | case BPRM_CHECK: | |
118 | iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED); | |
119 | break; | |
120 | case MODULE_CHECK: | |
121 | iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); | |
122 | break; | |
5a9196d7 MZ |
123 | case FIRMWARE_CHECK: |
124 | iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); | |
125 | break; | |
d79d72e0 MZ |
126 | case FILE_CHECK: |
127 | default: | |
128 | iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); | |
129 | break; | |
130 | } | |
131 | } | |
132 | ||
d3634d0f DK |
133 | void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, |
134 | struct ima_digest_data *hash) | |
135 | { | |
136 | struct signature_v2_hdr *sig; | |
137 | ||
3ea7a560 | 138 | if (!xattr_value || xattr_len < 2) |
d3634d0f DK |
139 | return; |
140 | ||
3ea7a560 DK |
141 | switch (xattr_value->type) { |
142 | case EVM_IMA_XATTR_DIGSIG: | |
143 | sig = (typeof(sig))xattr_value; | |
144 | if (sig->version != 2 || xattr_len <= sizeof(*sig)) | |
145 | return; | |
146 | hash->algo = sig->hash_algo; | |
147 | break; | |
148 | case IMA_XATTR_DIGEST_NG: | |
149 | hash->algo = xattr_value->digest[0]; | |
150 | break; | |
151 | case IMA_XATTR_DIGEST: | |
152 | /* this is for backward compatibility */ | |
153 | if (xattr_len == 21) { | |
154 | unsigned int zero = 0; | |
155 | if (!memcmp(&xattr_value->digest[16], &zero, 4)) | |
156 | hash->algo = HASH_ALGO_MD5; | |
157 | else | |
158 | hash->algo = HASH_ALGO_SHA1; | |
159 | } else if (xattr_len == 17) | |
160 | hash->algo = HASH_ALGO_MD5; | |
161 | break; | |
162 | } | |
d3634d0f DK |
163 | } |
164 | ||
165 | int ima_read_xattr(struct dentry *dentry, | |
166 | struct evm_ima_xattr_data **xattr_value) | |
167 | { | |
c6f493d6 | 168 | struct inode *inode = d_backing_inode(dentry); |
d3634d0f DK |
169 | |
170 | if (!inode->i_op->getxattr) | |
171 | return 0; | |
172 | ||
173 | return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, | |
174 | 0, GFP_NOFS); | |
175 | } | |
176 | ||
2fe5d6de MZ |
177 | /* |
178 | * ima_appraise_measurement - appraise file measurement | |
179 | * | |
180 | * Call evm_verifyxattr() to verify the integrity of 'security.ima'. | |
181 | * Assuming success, compare the xattr hash with the collected measurement. | |
182 | * | |
183 | * Return 0 on success, error code otherwise | |
184 | */ | |
d79d72e0 | 185 | int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, |
d3634d0f DK |
186 | struct file *file, const unsigned char *filename, |
187 | struct evm_ima_xattr_data *xattr_value, | |
3034a146 | 188 | int xattr_len, int opened) |
2fe5d6de | 189 | { |
52a13284 MZ |
190 | static const char op[] = "appraise_data"; |
191 | char *cause = "unknown"; | |
b583043e | 192 | struct dentry *dentry = file->f_path.dentry; |
c6f493d6 | 193 | struct inode *inode = d_backing_inode(dentry); |
2fe5d6de | 194 | enum integrity_status status = INTEGRITY_UNKNOWN; |
3ea7a560 | 195 | int rc = xattr_len, hash_start = 0; |
2fe5d6de | 196 | |
2fe5d6de MZ |
197 | if (!inode->i_op->getxattr) |
198 | return INTEGRITY_UNKNOWN; | |
199 | ||
2fe5d6de MZ |
200 | if (rc <= 0) { |
201 | if (rc && rc != -ENODATA) | |
202 | goto out; | |
203 | ||
204 | cause = "missing-hash"; | |
b151d6b0 | 205 | status = INTEGRITY_NOLABEL; |
3034a146 | 206 | if (opened & FILE_CREATED) { |
b151d6b0 DK |
207 | iint->flags |= IMA_NEW_FILE; |
208 | status = INTEGRITY_PASS; | |
209 | } | |
2fe5d6de MZ |
210 | goto out; |
211 | } | |
212 | ||
8606404f | 213 | status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); |
2fe5d6de MZ |
214 | if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { |
215 | if ((status == INTEGRITY_NOLABEL) | |
216 | || (status == INTEGRITY_NOXATTRS)) | |
217 | cause = "missing-HMAC"; | |
218 | else if (status == INTEGRITY_FAIL) | |
219 | cause = "invalid-HMAC"; | |
220 | goto out; | |
221 | } | |
8606404f | 222 | switch (xattr_value->type) { |
3ea7a560 DK |
223 | case IMA_XATTR_DIGEST_NG: |
224 | /* first byte contains algorithm id */ | |
225 | hash_start = 1; | |
8606404f | 226 | case IMA_XATTR_DIGEST: |
0e5a247c | 227 | if (iint->flags & IMA_DIGSIG_REQUIRED) { |
7e9001f6 | 228 | cause = "IMA-signature-required"; |
0e5a247c DK |
229 | status = INTEGRITY_FAIL; |
230 | break; | |
231 | } | |
3ea7a560 DK |
232 | if (xattr_len - sizeof(xattr_value->type) - hash_start >= |
233 | iint->ima_hash->length) | |
d3634d0f DK |
234 | /* xattr length may be longer. md5 hash in previous |
235 | version occupied 20 bytes in xattr, instead of 16 | |
236 | */ | |
3ea7a560 | 237 | rc = memcmp(&xattr_value->digest[hash_start], |
a35c3fb6 DK |
238 | iint->ima_hash->digest, |
239 | iint->ima_hash->length); | |
c7c8bb23 DK |
240 | else |
241 | rc = -EINVAL; | |
8606404f DK |
242 | if (rc) { |
243 | cause = "invalid-hash"; | |
244 | status = INTEGRITY_FAIL; | |
8606404f DK |
245 | break; |
246 | } | |
247 | status = INTEGRITY_PASS; | |
248 | break; | |
249 | case EVM_IMA_XATTR_DIGSIG: | |
250 | iint->flags |= IMA_DIGSIG; | |
251 | rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, | |
b1aaab22 | 252 | (const char *)xattr_value, rc, |
a35c3fb6 DK |
253 | iint->ima_hash->digest, |
254 | iint->ima_hash->length); | |
8606404f DK |
255 | if (rc == -EOPNOTSUPP) { |
256 | status = INTEGRITY_UNKNOWN; | |
257 | } else if (rc) { | |
258 | cause = "invalid-signature"; | |
259 | status = INTEGRITY_FAIL; | |
260 | } else { | |
261 | status = INTEGRITY_PASS; | |
262 | } | |
263 | break; | |
264 | default: | |
265 | status = INTEGRITY_UNKNOWN; | |
266 | cause = "unknown-ima-data"; | |
267 | break; | |
2fe5d6de | 268 | } |
8606404f | 269 | |
2fe5d6de MZ |
270 | out: |
271 | if (status != INTEGRITY_PASS) { | |
8606404f DK |
272 | if ((ima_appraise & IMA_APPRAISE_FIX) && |
273 | (!xattr_value || | |
274 | xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { | |
def3e8b9 DK |
275 | if (!ima_fix_xattr(dentry, iint)) |
276 | status = INTEGRITY_PASS; | |
2fe5d6de MZ |
277 | } |
278 | integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, | |
279 | op, cause, rc, 0); | |
8606404f | 280 | } else { |
d79d72e0 | 281 | ima_cache_flags(iint, func); |
2fe5d6de | 282 | } |
d79d72e0 | 283 | ima_set_cache_status(iint, func, status); |
2fe5d6de MZ |
284 | return status; |
285 | } | |
286 | ||
287 | /* | |
288 | * ima_update_xattr - update 'security.ima' hash value | |
289 | */ | |
290 | void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) | |
291 | { | |
b583043e | 292 | struct dentry *dentry = file->f_path.dentry; |
2fe5d6de MZ |
293 | int rc = 0; |
294 | ||
8606404f DK |
295 | /* do not collect and update hash for digital signatures */ |
296 | if (iint->flags & IMA_DIGSIG) | |
297 | return; | |
298 | ||
d3634d0f | 299 | rc = ima_collect_measurement(iint, file, NULL, NULL); |
2fe5d6de MZ |
300 | if (rc < 0) |
301 | return; | |
8606404f | 302 | |
2fe5d6de MZ |
303 | ima_fix_xattr(dentry, iint); |
304 | } | |
305 | ||
306 | /** | |
307 | * ima_inode_post_setattr - reflect file metadata changes | |
308 | * @dentry: pointer to the affected dentry | |
309 | * | |
310 | * Changes to a dentry's metadata might result in needing to appraise. | |
311 | * | |
312 | * This function is called from notify_change(), which expects the caller | |
313 | * to lock the inode's i_mutex. | |
314 | */ | |
315 | void ima_inode_post_setattr(struct dentry *dentry) | |
316 | { | |
c6f493d6 | 317 | struct inode *inode = d_backing_inode(dentry); |
2fe5d6de MZ |
318 | struct integrity_iint_cache *iint; |
319 | int must_appraise, rc; | |
320 | ||
a756024e | 321 | if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode) |
2fe5d6de MZ |
322 | || !inode->i_op->removexattr) |
323 | return; | |
324 | ||
325 | must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); | |
326 | iint = integrity_iint_find(inode); | |
327 | if (iint) { | |
d79d72e0 MZ |
328 | iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | |
329 | IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | | |
330 | IMA_ACTION_FLAGS); | |
2fe5d6de MZ |
331 | if (must_appraise) |
332 | iint->flags |= IMA_APPRAISE; | |
2fe5d6de MZ |
333 | } |
334 | if (!must_appraise) | |
335 | rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); | |
336 | return; | |
337 | } | |
42c63330 MZ |
338 | |
339 | /* | |
340 | * ima_protect_xattr - protect 'security.ima' | |
341 | * | |
342 | * Ensure that not just anyone can modify or remove 'security.ima'. | |
343 | */ | |
344 | static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, | |
345 | const void *xattr_value, size_t xattr_value_len) | |
346 | { | |
347 | if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) { | |
348 | if (!capable(CAP_SYS_ADMIN)) | |
349 | return -EPERM; | |
350 | return 1; | |
351 | } | |
352 | return 0; | |
353 | } | |
354 | ||
060bdebf | 355 | static void ima_reset_appraise_flags(struct inode *inode, int digsig) |
42c63330 MZ |
356 | { |
357 | struct integrity_iint_cache *iint; | |
358 | ||
a756024e | 359 | if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)) |
42c63330 MZ |
360 | return; |
361 | ||
362 | iint = integrity_iint_find(inode); | |
363 | if (!iint) | |
364 | return; | |
365 | ||
45e2472e | 366 | iint->flags &= ~IMA_DONE_MASK; |
060bdebf MZ |
367 | if (digsig) |
368 | iint->flags |= IMA_DIGSIG; | |
42c63330 MZ |
369 | return; |
370 | } | |
371 | ||
372 | int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, | |
373 | const void *xattr_value, size_t xattr_value_len) | |
374 | { | |
060bdebf | 375 | const struct evm_ima_xattr_data *xvalue = xattr_value; |
42c63330 MZ |
376 | int result; |
377 | ||
378 | result = ima_protect_xattr(dentry, xattr_name, xattr_value, | |
379 | xattr_value_len); | |
380 | if (result == 1) { | |
c68ed80c DK |
381 | bool digsig; |
382 | ||
a48fda9d DK |
383 | if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) |
384 | return -EINVAL; | |
c68ed80c DK |
385 | digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG); |
386 | if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE)) | |
387 | return -EPERM; | |
388 | ima_reset_appraise_flags(d_backing_inode(dentry), digsig); | |
42c63330 MZ |
389 | result = 0; |
390 | } | |
391 | return result; | |
392 | } | |
393 | ||
394 | int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) | |
395 | { | |
396 | int result; | |
397 | ||
398 | result = ima_protect_xattr(dentry, xattr_name, NULL, 0); | |
399 | if (result == 1) { | |
c6f493d6 | 400 | ima_reset_appraise_flags(d_backing_inode(dentry), 0); |
42c63330 MZ |
401 | result = 0; |
402 | } | |
403 | return result; | |
404 | } |