When in development it is useful to read back the IMA policy. This patch provides the functionality. However, this is a potential security hole so it should not be used in production-grade kernels.
Signed-off-by: Petko Manolov <pet...@mip-labs.com> --- security/integrity/ima/Kconfig | 13 +++ security/integrity/ima/ima.h | 13 ++- security/integrity/ima/ima_fs.c | 29 +++++- security/integrity/ima/ima_policy.c | 190 ++++++++++++++++++++++++++++++++++++ 4 files changed, 239 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 235b3c2..040778f 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -121,6 +121,19 @@ config IMA_WRITE_POLICY If unsure, say N. +config IMA_READ_POLICY + bool "Enable reading back the current IMA policy" + depends on IMA + default n + help + When developing and debugging an IMA enabled system it is often + useful to be able to read the IMA policy. This patch allows + for doing so. However, being able to read the IMA policy is + considered insecure and is strongly discouraged for + production-grade kernels. + + If unsure, say N. + config IMA_APPRAISE bool "Appraise integrity measurements" depends on IMA diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e2a60c3..ebc3554 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -166,6 +166,10 @@ void ima_update_policy(void); void ima_update_policy_flag(void); ssize_t ima_parse_add_rule(char *); void ima_delete_rules(void); +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); /* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 @@ -263,4 +267,11 @@ static inline int ima_init_keyring(const unsigned int id) return 0; } #endif /* CONFIG_IMA_TRUSTED_KEYRING */ -#endif + +#ifdef CONFIG_IMA_READ_POLICY +#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR) +#else +#define POLICY_FILE_FLAGS S_IWUSR +#endif /* CONFIG_IMA_WRITE_POLICY */ + +#endif /* __LINUX_IMA_H */ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index a3cf5c0..85bd129 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -311,14 +311,29 @@ enum ima_fs_flags { static unsigned long ima_fs_flags; +#ifdef CONFIG_IMA_READ_POLICY +static const struct seq_operations ima_policy_seqops = { + .start = ima_policy_start, + .next = ima_policy_next, + .stop = ima_policy_stop, + .show = ima_policy_show, +}; +#endif + /* * ima_open_policy: sequentialize access to the policy file */ static int ima_open_policy(struct inode *inode, struct file *filp) { - /* No point in being allowed to open it if you aren't going to write */ - if (!(filp->f_flags & O_WRONLY)) + if (!(filp->f_flags & O_WRONLY)) { +#ifndef CONFIG_IMA_READ_POLICY return -EACCES; +#else + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return seq_open(filp, &ima_policy_seqops); +#endif + } if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) return -EBUSY; return 0; @@ -334,6 +349,10 @@ static int ima_open_policy(struct inode *inode, struct file *filp) static int ima_release_policy(struct inode *inode, struct file *file) { const char *cause = valid_policy ? "completed" : "failed"; + int policy_write = (file->f_flags & O_WRONLY); + + if (!policy_write) + return 0; pr_info("IMA: policy update %s\n", cause); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, @@ -342,9 +361,9 @@ static int ima_release_policy(struct inode *inode, struct file *file) if (!valid_policy) { ima_delete_rules(); valid_policy = 1; - clear_bit(IMA_FS_BUSY, &ima_fs_flags); return 0; } + ima_update_policy(); #ifndef CONFIG_IMA_WRITE_POLICY securityfs_remove(ima_policy); @@ -358,6 +377,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, .write = ima_write_policy, + .read = seq_read, .release = ima_release_policy, .llseek = generic_file_llseek, }; @@ -395,8 +415,7 @@ int __init ima_fs_init(void) if (IS_ERR(violations)) goto out; - ima_policy = securityfs_create_file("policy", - S_IWUSR, + ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, ima_dir, NULL, &ima_measure_policy_ops); if (IS_ERR(ima_policy)) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 7ace4e4..29961d7 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/rculist.h> #include <linux/genhd.h> +#include <linux/seq_file.h> #include "ima.h" @@ -487,6 +488,35 @@ static match_table_t policy_tokens = { {Opt_err, NULL} }; +enum { + mask_err = -1, + mask_exec = 1, mask_write, mask_read, mask_append +}; + +static match_table_t mask_tokens = { + {mask_exec, "MAY_EXEC"}, + {mask_write, "MAY_WRITE"}, + {mask_read, "MAY_READ"}, + {mask_append, "MAY_APPEND"}, + {mask_err, NULL} +}; + +enum { + func_err = -1, + func_file = 1, func_mmap, func_bprm, + func_module, func_firmware, func_post +}; + +static match_table_t func_tokens = { + {func_file, "FILE_CHECK"}, + {func_mmap, "MMAP_CHECK"}, + {func_bprm, "BPRM_CHECK"}, + {func_module, "MODULE_CHECK"}, + {func_firmware, "FIRMWARE_CHECK"}, + {func_post, "POST_SETATTR"}, + {func_err, NULL} +}; + static int ima_lsm_rule_init(struct ima_rule_entry *entry, substring_t *args, int lsm_rule, int audit_type) { @@ -829,3 +859,163 @@ void ima_delete_rules(void) kfree(entry); } } + +void *ima_policy_start(struct seq_file *m, loff_t *pos) +{ + loff_t l = *pos; + struct ima_rule_entry *entry; + + rcu_read_lock(); + list_for_each_entry_rcu(entry, ima_rules, list) { + if (!l--) { + rcu_read_unlock(); + return entry; + } + } + rcu_read_unlock(); + return NULL; +} + +void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos) +{ + struct ima_rule_entry *entry = v; + + rcu_read_lock(); + entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list); + rcu_read_unlock(); + (*pos)++; + + return (&entry->list == ima_rules) ? NULL : entry; +} + +void ima_policy_stop(struct seq_file *m, void *v) +{ +} + +#define pt(token) policy_tokens[token-1].pattern +#define mt(token) mask_tokens[token-1].pattern +#define ft(token) func_tokens[token-1].pattern + +int ima_policy_show(struct seq_file *m, void *v) +{ + struct ima_rule_entry *entry = v; + int i = 0; + + rcu_read_lock(); + + if (entry->action & MEASURE) + seq_puts(m, pt(Opt_measure)); + if (entry->action & DONT_MEASURE) + seq_puts(m, pt(Opt_dont_measure)); + if (entry->action & APPRAISE) + seq_puts(m, pt(Opt_appraise)); + if (entry->action & DONT_APPRAISE) + seq_puts(m, pt(Opt_dont_appraise)); + if (entry->action & AUDIT) + seq_puts(m, pt(Opt_audit)); + + seq_puts(m, " "); + + if (entry->flags & IMA_FUNC) { + switch (entry->func) { + case FILE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_file)); + break; + case MMAP_CHECK: + seq_printf(m, pt(Opt_func), ft(func_mmap)); + break; + case BPRM_CHECK: + seq_printf(m, pt(Opt_func), ft(func_bprm)); + break; + case MODULE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_module)); + break; + case FIRMWARE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_firmware)); + break; + case POST_SETATTR: + seq_printf(m, pt(Opt_func), ft(func_post)); + break; + default: + seq_printf(m, "func=%d", entry->func); + break; + } + seq_puts(m, " "); + } + + if (entry->flags & IMA_MASK) { + if (entry->mask & MAY_EXEC) + seq_printf(m, pt(Opt_mask), mt(mask_exec)); + if (entry->mask & MAY_WRITE) + seq_printf(m, pt(Opt_mask), mt(mask_write)); + if (entry->mask & MAY_READ) + seq_printf(m, pt(Opt_mask), mt(mask_read)); + if (entry->mask & MAY_APPEND) + seq_printf(m, pt(Opt_mask), mt(mask_append)); + seq_puts(m, " "); + } + + if (entry->flags & IMA_FSMAGIC) { + seq_printf(m, "fsmagic=0x%lx", entry->fsmagic); + seq_puts(m, " "); + } + + if (entry->flags & IMA_FSUUID) { + seq_puts(m, "fsuuid="); + for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) { + switch (i) { + case 4: + case 6: + case 8: + case 10: + seq_puts(m, "-"); + } + seq_printf(m, "%x", entry->fsuuid[i]); + } + seq_puts(m, " "); + } + + if (entry->flags & IMA_UID) { + seq_printf(m, "uid=%d", __kuid_val(entry->uid)); + seq_puts(m, " "); + } + + if (entry->flags & IMA_FOWNER) { + seq_printf(m, "fowner=%d", __kuid_val(entry->fowner)); + seq_puts(m, " "); + } + + for (i = 0; i < MAX_LSM_RULES; i++) { + if (entry->lsm[i].rule) { + switch (i) { + case LSM_OBJ_USER: + seq_printf(m, pt(Opt_obj_user), + (char *)entry->lsm[i].args_p); + break; + case LSM_OBJ_ROLE: + seq_printf(m, pt(Opt_obj_role), + (char *)entry->lsm[i].args_p); + break; + case LSM_OBJ_TYPE: + seq_printf(m, pt(Opt_obj_type), + (char *)entry->lsm[i].args_p); + break; + case LSM_SUBJ_USER: + seq_printf(m, pt(Opt_subj_user), + (char *)entry->lsm[i].args_p); + break; + case LSM_SUBJ_ROLE: + seq_printf(m, pt(Opt_subj_role), + (char *)entry->lsm[i].args_p); + break; + case LSM_SUBJ_TYPE: + seq_printf(m, pt(Opt_subj_type), + (char *)entry->lsm[i].args_p); + break; + } + } + } + rcu_read_unlock(); + seq_puts(m, "\n"); + return 0; +} -- 2.6.1 -- To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html