New ima_ns_policy structure to describe IMA policy data per namespace. Using a radix tree to map namespace ids to a respective ima_ns_policy structure. When it is needed to retrieve IMA policy rules/flags, the target ima_ns_policy structure is retrieved from the radix tree by getting the namespace id from the current context. Signed-off-by: Guilherme Magalhaes <guilherme.magalhaes@xxxxxxx> --- security/integrity/ima/ima.h | 37 +++++++++++++++++ security/integrity/ima/ima_fs.c | 79 ++++++++++++++++++++++++++++++++++--- security/integrity/ima/ima_init.c | 2 + security/integrity/ima/ima_policy.c | 29 +++++++++----- 4 files changed, 133 insertions(+), 14 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6e8ca8e..1c5c875 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -140,6 +140,21 @@ static inline void ima_load_kexec_buffer(void) {} */ extern bool ima_canonical_fmt; +/* Namespace policy globals */ +struct ima_ns_policy { + struct dentry *policy_dentry; + struct dentry *ns_dentry; + struct list_head *ima_rules; + struct list_head ima_policy_rules; + int ima_policy_flag; + int ima_appraise; +}; + +#ifdef CONFIG_IMA_PER_NAMESPACE +extern spinlock_t ima_ns_policy_lock; +extern struct radix_tree_root ima_ns_policy_mapping; +#endif + /* Internal IMA function definitions */ int ima_init(void); int ima_fs_init(void); @@ -166,6 +181,27 @@ int ima_measurements_show(struct seq_file *m, void *v); unsigned long ima_get_binary_runtime_size(void); int ima_init_template(void); void ima_init_template_list(void); +#ifdef CONFIG_IMA_PER_NAMESPACE +static inline void ima_namespace_lock_init(void) { + spin_lock_init(&ima_ns_policy_lock); +} +static inline void ima_namespace_lock(void) { + spin_lock(&ima_ns_policy_lock); +} +static inline void ima_namespace_unlock(void) { + spin_unlock(&ima_ns_policy_lock); +} +#else +static inline void ima_namespace_lock_init(void) { + return; +} +static inline void ima_namespace_lock(void) { + return; +} +static inline void ima_namespace_unlock(void) { + return; +} +#endif /* * used to protect h_table and sha_table @@ -226,6 +262,7 @@ 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_free_policy_rules(struct list_head *policy_rules); int ima_check_policy(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); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 6456407..ce6dcdf 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -275,6 +275,48 @@ static const struct file_operations ima_ascii_measurements_ops = { }; #ifdef CONFIG_IMA_PER_NAMESPACE +/* used for namespace policy rules initialization */ +static LIST_HEAD(empty_policy); + +static int allocate_namespace_policy(struct ima_ns_policy **ins, + struct dentry *policy_dentry, struct dentry *ns_dentry) +{ + int result; + struct ima_ns_policy *p; + + p = kmalloc(sizeof(struct ima_ns_policy), GFP_KERNEL); + if (!p) { + result = -ENOMEM; + goto out; + } + + p->policy_dentry = policy_dentry; + p->ns_dentry = ns_dentry; + p->ima_appraise = 0; + p->ima_policy_flag = 0; + INIT_LIST_HEAD(&p->ima_policy_rules); + /* namespace starts with empty rules and not pointing to + * ima_policy_rules */ + p->ima_rules = &empty_policy; + + result = 0; + *ins = p; + +out: + return result; +} + +static void free_namespace_policy(struct ima_ns_policy *ins) +{ + if (ins->policy_dentry) + securityfs_remove(ins->policy_dentry); + securityfs_remove(ins->ns_dentry); + + ima_free_policy_rules(&ins->ima_policy_rules); + + kfree(ins); +} + /* * check_mntns: check a mount namespace is valid * @@ -476,9 +518,11 @@ static int ima_release_policy(struct inode *inode, struct file *file) #ifndef CONFIG_IMA_WRITE_POLICY securityfs_remove(ima_policy); ima_policy = NULL; -#else - clear_bit(IMA_FS_BUSY, &ima_fs_flags); #endif + + /* always clear the busy flag so other namespaces can use it */ + clear_bit(IMA_FS_BUSY, &ima_fs_flags); + return 0; } @@ -500,11 +544,14 @@ static int create_mnt_ns_directory(unsigned int ns_id) int result; struct dentry *ns_dir, *ns_policy; char dir_name[64]; + struct ima_ns_policy *ins; snprintf(dir_name, sizeof(dir_name), "%u", ns_id); ns_dir = securityfs_create_dir(dir_name, ima_dir); if (IS_ERR(ns_dir)) { + /* TODO: handle EEXIST error, remove the folder and + continue the procedure */ result = PTR_ERR(ns_dir); goto out; } @@ -518,7 +565,15 @@ static int create_mnt_ns_directory(unsigned int ns_id) goto out; } - result = 0; + result = allocate_namespace_policy(&ins, ns_policy, ns_dir); + if (!result) { + result = radix_tree_insert(&ima_ns_policy_mapping, ns_id, ins); + if (result) + free_namespace_policy(ins); + } else { + securityfs_remove(ns_policy); + securityfs_remove(ns_dir); + } out: return result; @@ -528,6 +583,7 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen) { unsigned int ns_id; ssize_t result; + struct ima_ns_policy *ins; result = -EINVAL; @@ -536,21 +592,34 @@ static ssize_t handle_new_namespace_policy(const char *data, size_t datalen) goto out; } + rcu_read_lock(); + ins = radix_tree_lookup(&ima_ns_policy_mapping, ns_id); + rcu_read_unlock(); + if (ins) { + pr_info("IMA: directory for namespace id %u already created\n", ns_id); + result = datalen; + goto out; + } + + ima_namespace_lock(); if (check_mntns(ns_id)) { result = -ENOENT; pr_err("IMA: unused namespace id %u\n", ns_id); - goto out; + goto out_unlock; } result = create_mnt_ns_directory(ns_id); if (result != 0) { pr_err("IMA: namespace id %u directory creation failed\n", ns_id); - goto out; + goto out_unlock; } result = datalen; pr_info("IMA: directory created for namespace id %u\n", ns_id); +out_unlock: + ima_namespace_unlock(); + out: return result; } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 2967d49..b557ee3 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -135,6 +135,8 @@ int __init ima_init(void) if (rc != 0) return rc; + ima_namespace_lock_init(); + ima_init_policy(); return ima_fs_init(); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index aed47b7..2e8c3b7 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -47,6 +47,12 @@ int ima_policy_flag; static int temp_ima_appraise; +#ifdef CONFIG_IMA_PER_NAMESPACE +/* policy namespace map entries except the initial namespace policy */ +RADIX_TREE(ima_ns_policy_mapping, GFP_ATOMIC); +spinlock_t ima_ns_policy_lock; +#endif + #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE @@ -863,19 +869,12 @@ ssize_t ima_parse_add_rule(char *rule) return len; } -/** - * ima_delete_rules() called to cleanup invalid in-flight policy. - * We don't need locking as we operate on the temp list, which is - * different from the active one. There is also only one user of - * ima_delete_rules() at a time. - */ -void ima_delete_rules(void) +void ima_free_policy_rules(struct list_head *policy_rules) { struct ima_rule_entry *entry, *tmp; int i; - temp_ima_appraise = 0; - list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) { + list_for_each_entry_safe(entry, tmp, policy_rules, list) { for (i = 0; i < MAX_LSM_RULES; i++) kfree(entry->lsm[i].args_p); @@ -884,6 +883,18 @@ void ima_delete_rules(void) } } +/** + * ima_delete_rules() called to cleanup invalid in-flight policy. + * We don't need locking as we operate on the temp list, which is + * different from the active one. There is also only one user of + * ima_delete_rules() at a time. + */ +void ima_delete_rules(void) +{ + temp_ima_appraise = 0; + ima_free_policy_rules(&ima_temp_rules); +} + #ifdef CONFIG_IMA_READ_POLICY enum { mask_exec = 0, mask_write, mask_read, mask_append -- 2.7.4