The first write on the ima policy file permits to override the default policy defined with the ima_policy= boot parameter. This can be done by adding the /etc/ima/ima-policy which allows loading the custom policy during boot. It is also possible to load custom policy at runtime through file operations: cp custom_ima_policy /sys/kernel/security/ima/policy cat custom_ima_policy > /sys/kernel/security/ima/policy or by writing the absolute path of the file containing the custom policy: echo /path/of/custom_ima_policy > /sys/kernel/security/ima/policy In these cases, file signature can be necessary to load the policy (func=POLICY_CHECK). Custom policy can also be set at runtime by directly writing the policy stream on the ima policy file: echo -e "measure func=BPRM_CHECK mask=MAY_EXEC\n" \ "audit func=BPRM_CHECK mask=MAY_EXEC\n" \ > /sys/kernel/security/ima/policy In this case, there is no mechanism to verify the integrity of the new policy. Add a new entry in the ima measurements list containing the ascii custom ima policy buffer when not verified at load time. Signed-off-by: Enrico Bravi <enrico.bravi@xxxxxxxxx> --- security/integrity/ima/ima.h | 3 ++ security/integrity/ima/ima_fs.c | 11 ++++ security/integrity/ima/ima_policy.c | 81 ++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c0d3b716d11f..27cba2e612a5 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -46,6 +46,8 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 }; /* current content of the policy */ extern int ima_policy_flag; +extern bool override_ima_policy; + /* bitset of digests algorithms allowed in the setxattr hook */ extern atomic_t ima_setxattr_allowed_hash_algorithms; @@ -414,6 +416,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos); void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos); void ima_policy_stop(struct seq_file *m, void *v); int ima_policy_show(struct seq_file *m, void *v); +void ima_measure_override_policy(size_t file_len); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index e4a79a9b2d58..8c516de4aebe 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -309,6 +309,9 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, }; +static size_t text_policy_len; +bool override_ima_policy; + static ssize_t ima_read_policy(char *path) { void *data = NULL; @@ -383,6 +386,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, result = -EACCES; } else { result = ima_parse_add_rule(data); + text_policy_len += (result + 1); } mutex_unlock(&ima_write_mutex); out_free: @@ -532,6 +536,10 @@ static int ima_release_policy(struct inode *inode, struct file *file) } ima_update_policy(); + if (unlikely(override_ima_policy && text_policy_len)) { + ima_measure_override_policy(text_policy_len); + override_ima_policy = false; + } #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY) securityfs_remove(ima_policy); ima_policy = NULL; @@ -558,6 +566,9 @@ int __init ima_fs_init(void) ascii_securityfs_measurement_lists = NULL; binary_securityfs_measurement_lists = NULL; + text_policy_len = 0; + override_ima_policy = false; + ima_dir = securityfs_create_dir("ima", integrity_dir); if (IS_ERR(ima_dir)) return PTR_ERR(ima_dir); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 21a8e54c383f..34753bce4668 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -17,6 +17,7 @@ #include <linux/slab.h> #include <linux/rculist.h> #include <linux/seq_file.h> +#include <linux/vmalloc.h> #include <linux/ima.h> #include "ima.h" @@ -1044,6 +1045,8 @@ void ima_update_policy(void) if (ima_rules != (struct list_head __rcu *)policy) { ima_policy_flag = 0; + override_ima_policy = true; + rcu_assign_pointer(ima_rules, policy); /* * IMA architecture specific policy rules are specified @@ -1982,7 +1985,6 @@ const char *const func_tokens[] = { __ima_hooks(__ima_hook_stringify) }; -#ifdef CONFIG_IMA_READ_POLICY enum { mask_exec = 0, mask_write, mask_read, mask_append }; @@ -1994,6 +1996,7 @@ static const char *const mask_tokens[] = { "^MAY_APPEND" }; +#ifdef CONFIG_IMA_READ_POLICY void *ima_policy_start(struct seq_file *m, loff_t *pos) { loff_t l = *pos; @@ -2028,6 +2031,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) void ima_policy_stop(struct seq_file *m, void *v) { } +#endif /* CONFIG_IMA_READ_POLICY */ #define pt(token) policy_tokens[token].pattern #define mt(token) mask_tokens[token] @@ -2276,7 +2280,6 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, "\n"); return 0; } -#endif /* CONFIG_IMA_READ_POLICY */ #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) /* @@ -2333,3 +2336,77 @@ bool ima_appraise_signature(enum kernel_read_file_id id) return found; } #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */ + +void ima_measure_override_policy(size_t file_len) +{ + struct ima_iint_cache iint = {}; + const char event_name[] = "ima-policy-override"; + struct ima_event_data event_data = {.iint = &iint, + .filename = event_name}; + struct ima_max_digest_data hash; + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); + static const char op[] = "measure_ima_policy_override"; + struct ima_template_entry *entry = NULL; + static char *audit_cause = "ENOMEM"; + struct ima_template_desc *template; + struct ima_rule_entry *rule_entry; + struct list_head *ima_rules_tmp; + struct seq_file file; + int result = -ENOMEM; + int violation = 0; + + file.buf = vmalloc(file_len); + if (!file.buf) + goto out; + + file.read_pos = 0; + file.size = file_len; + file.count = 0; + + rcu_read_lock(); + ima_rules_tmp = rcu_dereference(ima_rules); + list_for_each_entry_rcu(rule_entry, ima_rules_tmp, list) { + ima_policy_show(&file, rule_entry); + } + rcu_read_unlock(); + + event_data.buf = file.buf; + event_data.buf_len = file.count; + + template = ima_template_desc_buf(); + if (!template) { + audit_cause = "ima_template_desc_buf"; + goto out_free; + } + + iint.ima_hash = hash_hdr; + iint.ima_hash->algo = ima_hash_algo; + iint.ima_hash->length = hash_digest_size[ima_hash_algo]; + + result = ima_calc_buffer_hash(file.buf, file.count, iint.ima_hash); + if (result < 0) { + audit_cause = "hashing_error"; + goto out_free; + } + + result = ima_alloc_init_template(&event_data, &entry, template); + if (result < 0) { + audit_cause = "alloc_entry"; + goto out_free; + } + + result = ima_store_template(entry, violation, NULL, event_data.buf, + CONFIG_IMA_MEASURE_PCR_IDX); + if (result < 0) { + audit_cause = "store_entry"; + ima_free_template_entry(entry); + } + +out_free: + kvfree(file.buf); +out: + if (result < 0) + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, event_name, + op, audit_cause, result, 1); +} base-commit: ffd294d346d185b70e28b1a28abe367bbfe53c04 -- 2.47.1