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 | extern void ima_post_create_tmpfile(struct inode *inode); | 18 | extern void ima_post_create_tmpfile(struct inode *inode); |
| 19 | extern void ima_file_free(struct file *file); | 19 | extern void ima_file_free(struct file *file); |
| 20 | extern int ima_file_mmap(struct file *file, unsigned long prot); | 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 | extern int ima_load_data(enum kernel_load_data_id id); | 22 | extern int ima_load_data(enum kernel_load_data_id id); |
| 22 | extern int ima_read_file(struct file *file, enum kernel_read_file_id id); | 23 | extern int ima_read_file(struct file *file, enum kernel_read_file_id id); |
| 23 | extern int ima_post_read_file(struct file *file, void *buf, loff_t size, | 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 | return 0; | 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 | static inline int ima_load_data(enum kernel_load_data_id id) | 80 | static inline int ima_load_data(enum kernel_load_data_id id) |
| 74 | { | 81 | { |
| 75 | return 0; | 82 | return 0; |
| @@ -393,6 +393,57 @@ int ima_file_mmap(struct file *file, unsigned long prot) · 392 unmodified lines | |||
| 393 | return 0; | 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 | * ima_bprm_check - based on policy, collect/store measurement. | 448 | * ima_bprm_check - based on policy, collect/store measurement. |
| 398 | * @bprm: contains the linux_binprm structure | 449 | * @bprm: contains the linux_binprm structure |
| @@ -1512,7 +1512,12 @@ int security_mmap_addr(unsigned long addr) · 1511 unmodified lines | |||
| 1512 | int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, | 1512 | int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, |
| 1513 | unsigned long prot) | 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 | int security_file_lock(struct file *file, unsigned int cmd) | 1523 | int security_file_lock(struct file *file, unsigned int cmd) |