Files can be mmap'ed read/write and later changed to execute to circumvent IMA's mmap appraise policy rules. Due to locking issues (mmap semaphore would be taken prior to i_mutex), files can not be measured or appraised at this point. Eliminate this integrity gap, by denying the mprotect PROT_EXECUTE change, if an mmap appraise policy rule exists. On mprotect change success, return 0. On failure, return -EACESS.
Unified · Split
| @@ -18,6 +18,7 @@ extern int ima_file_check(struct file *file, int mask); · 17 unmodified lines | ||
| 18 | 18 | extern void ima_post_create_tmpfile(struct inode *inode); |
| 19 | 19 | extern void ima_file_free(struct file *file); |
| 20 | 20 | extern int ima_file_mmap(struct file *file, unsigned long prot); |
| 21 | +extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot); | |
| 21 | 22 | extern int ima_load_data(enum kernel_load_data_id id); |
| 22 | 23 | extern int ima_read_file(struct file *file, enum kernel_read_file_id id); |
| 23 | 24 | extern int ima_post_read_file(struct file *file, void *buf, loff_t size, |
| @@ -70,6 +71,12 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) · 46 unmodified lines | ||
| 70 | 71 | return 0; |
| 71 | 72 | } |
| 72 | 73 | |
| 74 | +static inline int ima_file_mprotect(struct vm_area_struct *vma, | |
| 75 | + unsigned long prot) | |
| 76 | +{ | |
| 77 | + return 0; | |
| 78 | +} | |
| 79 | + | |
| 73 | 80 | static inline int ima_load_data(enum kernel_load_data_id id) |
| 74 | 81 | { |
| 75 | 82 | return 0; |
| @@ -393,6 +393,57 @@ int ima_file_mmap(struct file *file, unsigned long prot) · 392 unmodified lines | ||
| 393 | 393 | return 0; |
| 394 | 394 | } |
| 395 | 395 | |
| 396 | +/** | |
| 397 | + * ima_file_mprotect - based on policy, limit mprotect change | |
| 398 | + * @prot: contains the protection that will be applied by the kernel. | |
| 399 | + * | |
| 400 | + * Files can be mmap'ed read/write and later changed to execute to circumvent | |
| 401 | + * IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore | |
| 402 | + * would be taken before i_mutex), files can not be measured or appraised at | |
| 403 | + * this point. Eliminate this integrity gap by denying the mprotect | |
| 404 | + * PROT_EXECUTE change, if an mmap appraise policy rule exists. | |
| 405 | + * | |
| 406 | + * On mprotect change success, return 0. On failure, return -EACESS. | |
| 407 | + */ | |
| 408 | +int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot) | |
| 409 | +{ | |
| 410 | + struct ima_template_desc *template; | |
| 411 | + struct file *file = vma->vm_file; | |
| 412 | + char filename[NAME_MAX]; | |
| 413 | + char *pathbuf = NULL; | |
| 414 | + const char *pathname = NULL; | |
| 415 | + struct inode *inode; | |
| 416 | + int result = 0; | |
| 417 | + int action; | |
| 418 | + u32 secid; | |
| 419 | + int pcr; | |
| 420 | + | |
| 421 | + /* Is mprotect making an mmap'ed file executable? */ | |
| 422 | + if (!vma->vm_file || !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC)) | |
| 423 | + return 0; | |
| 424 | + | |
| 425 | + security_task_getsecid(current, &secid); | |
| 426 | + inode = file_inode(vma->vm_file); | |
| 427 | + action = ima_get_action(inode, current_cred(), secid, MAY_EXEC, | |
| 428 | + MMAP_CHECK, &pcr, &template, 0); | |
| 429 | + | |
| 430 | + /* Is the mmap'ed file in policy? */ | |
| 431 | + if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK))) | |
| 432 | + return 0; | |
| 433 | + | |
| 434 | + if (action & IMA_APPRAISE_SUBMASK) | |
| 435 | + result = -EPERM; | |
| 436 | + | |
| 437 | + file = vma->vm_file; | |
| 438 | + pathname = ima_d_path(&file->f_path, &pathbuf, filename); | |
| 439 | + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, pathname, | |
| 440 | + "collect_data", "failed-mprotect", result, 0); | |
| 441 | + if (pathbuf) | |
| 442 | + __putname(pathbuf); | |
| 443 | + | |
| 444 | + return result; | |
| 445 | +} | |
| 446 | + | |
| 396 | 447 | /** |
| 397 | 448 | * ima_bprm_check - based on policy, collect/store measurement. |
| 398 | 449 | * @bprm: contains the linux_binprm structure |
| @@ -1512,7 +1512,12 @@ int security_mmap_addr(unsigned long addr) · 1511 unmodified lines | ||
| 1512 | 1512 | int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, |
| 1513 | 1513 | unsigned long prot) |
| 1514 | 1514 | { |
| 1515 | − return call_int_hook(file_mprotect, 0, vma, reqprot, prot); | |
| 1515 | + int ret; | |
| 1516 | + | |
| 1517 | + ret = call_int_hook(file_mprotect, 0, vma, reqprot, prot); | |
| 1518 | + if (ret) | |
| 1519 | + return ret; | |
| 1520 | + return ima_file_mprotect(vma, prot); | |
| 1516 | 1521 | } |
| 1517 | 1522 | |
| 1518 | 1523 | int security_file_lock(struct file *file, unsigned int cmd) |