summaryrefslogtreecommitdiff
path: root/security/integrity/ima/ima_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/integrity/ima/ima_main.c')
-rw-r--r--security/integrity/ima/ima_main.c96
1 files changed, 85 insertions, 11 deletions
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 9d0abedeae77..8a91711ca79b 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -394,6 +394,58 @@ int ima_file_mmap(struct file *file, unsigned long prot)
}
/**
+ * ima_file_mprotect - based on policy, limit mprotect change
+ * @prot: contains the protection that will be applied by the kernel.
+ *
+ * Files can be mmap'ed read/write and later changed to execute to circumvent
+ * IMA's mmap appraisal policy rules. Due to locking issues (mmap semaphore
+ * would be taken before 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.
+ */
+int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
+{
+ struct ima_template_desc *template;
+ struct file *file = vma->vm_file;
+ char filename[NAME_MAX];
+ char *pathbuf = NULL;
+ const char *pathname = NULL;
+ struct inode *inode;
+ int result = 0;
+ int action;
+ u32 secid;
+ int pcr;
+
+ /* Is mprotect making an mmap'ed file executable? */
+ if (!(ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
+ !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
+ return 0;
+
+ security_task_getsecid(current, &secid);
+ inode = file_inode(vma->vm_file);
+ action = ima_get_action(inode, current_cred(), secid, MAY_EXEC,
+ MMAP_CHECK, &pcr, &template, 0);
+
+ /* Is the mmap'ed file in policy? */
+ if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
+ return 0;
+
+ if (action & IMA_APPRAISE_SUBMASK)
+ result = -EPERM;
+
+ file = vma->vm_file;
+ pathname = ima_d_path(&file->f_path, &pathbuf, filename);
+ integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, pathname,
+ "collect_data", "failed-mprotect", result, 0);
+ if (pathbuf)
+ __putname(pathbuf);
+
+ return result;
+}
+
+/**
* ima_bprm_check - based on policy, collect/store measurement.
* @bprm: contains the linux_binprm structure
*
@@ -674,6 +726,7 @@ int ima_load_data(enum kernel_load_data_id id)
/*
* process_buffer_measurement - Measure the buffer to ima log.
+ * @inode: inode associated with the object being measured (NULL for KEY_CHECK)
* @buf: pointer to the buffer that needs to be added to the log.
* @size: size of buffer(in bytes).
* @eventname: event name to be used for the buffer entry.
@@ -683,11 +736,12 @@ int ima_load_data(enum kernel_load_data_id id)
*
* Based on policy, the buffer is measured into the ima log.
*/
-void process_buffer_measurement(const void *buf, int size,
+void process_buffer_measurement(struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
int pcr, const char *keyring)
{
int ret = 0;
+ const char *audit_cause = "ENOMEM";
struct ima_template_entry *entry = NULL;
struct integrity_iint_cache iint = {};
struct ima_event_data event_data = {.iint = &iint,
@@ -715,7 +769,7 @@ void process_buffer_measurement(const void *buf, int size,
*/
if (func) {
security_task_getsecid(current, &secid);
- action = ima_get_action(NULL, current_cred(), secid, 0, func,
+ action = ima_get_action(inode, current_cred(), secid, 0, func,
&pcr, &template, keyring);
if (!(action & IMA_MEASURE))
return;
@@ -742,37 +796,54 @@ void process_buffer_measurement(const void *buf, int size,
iint.ima_hash->length = hash_digest_size[ima_hash_algo];
ret = ima_calc_buffer_hash(buf, size, iint.ima_hash);
- if (ret < 0)
+ if (ret < 0) {
+ audit_cause = "hashing_error";
goto out;
+ }
ret = ima_alloc_init_template(&event_data, &entry, template);
- if (ret < 0)
+ if (ret < 0) {
+ audit_cause = "alloc_entry";
goto out;
+ }
ret = ima_store_template(entry, violation, NULL, buf, pcr);
-
- if (ret < 0)
+ if (ret < 0) {
+ audit_cause = "store_entry";
ima_free_template_entry(entry);
+ }
out:
if (ret < 0)
- pr_devel("%s: failed, result: %d\n", __func__, ret);
+ integrity_audit_message(AUDIT_INTEGRITY_PCR, NULL, eventname,
+ func_measure_str(func),
+ audit_cause, ret, 0, ret);
return;
}
/**
* ima_kexec_cmdline - measure kexec cmdline boot args
+ * @kernel_fd: file descriptor of the kexec kernel being loaded
* @buf: pointer to buffer
* @size: size of buffer
*
* Buffers can only be measured, not appraised.
*/
-void ima_kexec_cmdline(const void *buf, int size)
+void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
{
- if (buf && size != 0)
- process_buffer_measurement(buf, size, "kexec-cmdline",
- KEXEC_CMDLINE, 0, NULL);
+ struct fd f;
+
+ if (!buf || !size)
+ return;
+
+ f = fdget(kernel_fd);
+ if (!f.file)
+ return;
+
+ process_buffer_measurement(file_inode(f.file), buf, size,
+ "kexec-cmdline", KEXEC_CMDLINE, 0, NULL);
+ fdput(f);
}
static int __init init_ima(void)
@@ -792,6 +863,9 @@ static int __init init_ima(void)
error = ima_init();
}
+ if (error)
+ return error;
+
error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier);
if (error)
pr_warn("Couldn't register LSM notifier, error %d\n", error);