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