From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add a list of the installed ima namespaces. IMA namespace is considered installed, if there is at least one process born in that namespace.
This list will be used to check the read-write violations and to detect any object related changes relevant across namespaces.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 8 ++- security/integrity/ima/ima.h | 11 ++++ security/integrity/ima/ima_init.c | 5 ++ security/integrity/ima/ima_main.c | 3 + security/integrity/ima/ima_ns.c | 101 ++++++++++++++++++++++++++++-- 5 files changed, 122 insertions(+), 6 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 67af79961e4e..77e7f512f61b 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -11,10 +11,12 @@ #include <linux/fs.h> #include <linux/security.h> #include <linux/kexec.h> -struct linux_binprm;
+struct linux_binprm; struct nsproxy; struct task_struct; +struct list_head; +struct llist_node;
#ifdef CONFIG_IMA extern int ima_bprm_check(struct linux_binprm *bprm); @@ -206,6 +208,10 @@ struct ima_namespace { struct ns_common ns; struct ucounts *ucounts; struct user_namespace *user_ns; + struct list_head list; + struct llist_node cleanup_list; /* namespaces on a death row */ + atomic_t inactive; /* set only when ns is added to the cleanup list */ + bool frozen; } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index dc536da8058e..da7e732b7e80 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -363,12 +363,23 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c
#endif /* CONFIG_IMA_APPRAISE */
+extern struct list_head ima_ns_list; +extern struct rw_semaphore ima_ns_list_lock; + #ifdef CONFIG_IMA_NS +int __init ima_init_namespace(void); + static inline struct ima_namespace *get_current_ns(void) { return current->nsproxy->ima_ns; } #else +static inline int __init ima_init_namespace(void) +{ + list_add_tail(&init_ima_ns.list, &ima_ns_list); + return 0; +} + static inline struct ima_namespace *get_current_ns(void) { return &init_ima_ns; diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index b1c341e239a8..2238fb21eaf0 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -32,6 +32,7 @@ struct ima_namespace init_ima_ns = { #ifdef CONFIG_IMA_NS .ns.ops = &imans_operations, #endif + .frozen = true }; EXPORT_SYMBOL(init_ima_ns);
@@ -158,6 +159,10 @@ int __init ima_init(void)
ima_init_policy();
+ rc = ima_init_namespace(); + if (rc != 0) + return rc; + rc = ima_fs_init(); if (rc != 0) return rc; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index f3242727634a..fbec552886c1 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -47,6 +47,9 @@ bool ima_plus_standard_pcr;
static int hash_setup_done;
+DECLARE_RWSEM(ima_ns_list_lock); +LIST_HEAD(ima_ns_list); + static struct notifier_block ima_lsm_policy_notifier = { .notifier_call = ima_lsm_policy_change, }; diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 8f5f301406a2..3a98cd536d05 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -21,9 +21,20 @@ #include <linux/user_namespace.h> #include <linux/nsproxy.h> #include <linux/sched.h> +#include <linux/list.h> +#include <linux/llist.h> +#include <linux/rwsem.h> +#include <linux/workqueue.h> +#include <linux/mutex.h>
#include "ima.h"
+static LLIST_HEAD(cleanup_list); +static struct workqueue_struct *imans_wq; + +/* Protects tasks entering the same, not yet active namespace */ +static DEFINE_MUTEX(frozen_lock); + static struct ucounts *inc_ima_namespaces(struct user_namespace *ns) { return inc_ucount(ns, current_euid(), UCOUNT_IMA_NAMESPACES); @@ -78,6 +89,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, ns->ns.ops = &imans_operations; ns->user_ns = get_user_ns(user_ns); ns->ucounts = ucounts; + ns->frozen = false;
return ns;
@@ -109,6 +121,19 @@ struct ima_namespace *copy_ima_ns(unsigned long flags, return clone_ima_ns(user_ns, old_ns); }
+int __init ima_init_namespace(void) +{ + /* Create workqueue for cleanup */ + imans_wq = create_singlethread_workqueue("imans"); + if (unlikely(!imans_wq)) + return -ENOMEM; + + /* No other reader or writer at this stage */ + list_add_tail(&init_ima_ns.list, &ima_ns_list); + + return 0; +} + static void destroy_ima_ns(struct ima_namespace *ns) { dec_ima_namespaces(ns->ucounts); @@ -117,13 +142,46 @@ static void destroy_ima_ns(struct ima_namespace *ns) kfree(ns); }
+static void cleanup_ima(struct work_struct *work) +{ + struct ima_namespace *ima_ns, *tmp; + struct llist_node *ima_kill_list; + + /* Atomically snapshot the list of namespaces to cleanup */ + ima_kill_list = llist_del_all(&cleanup_list); + + /* Remove ima namespace from the namespace list */ + down_write(&ima_ns_list_lock); + llist_for_each_entry(ima_ns, ima_kill_list, cleanup_list) + list_del(&ima_ns->list); + up_write(&ima_ns_list_lock); + + /* After removing ima namespace from the ima_ns_list, memory can be + * freed. At this stage nothing should keep a reference to the given + * namespace. + */ + llist_for_each_entry_safe(ima_ns, tmp, ima_kill_list, cleanup_list) + destroy_ima_ns(ima_ns); +} + +static DECLARE_WORK(ima_cleanup_work, cleanup_ima); + void free_ima_ns(struct kref *kref) { - struct ima_namespace *ns; + struct ima_namespace *ima_ns;
- ns = container_of(kref, struct ima_namespace, kref); + ima_ns = container_of(kref, struct ima_namespace, kref); + /* Namespace can be destroyed instantly if no process ever was born + * into it - it was never added to the ima_ns_list. + */ + if (!ima_ns->frozen) { + destroy_ima_ns(ima_ns); + return; + }
- destroy_ima_ns(ns); + atomic_set(&ima_ns->inactive, 1); + if (llist_add(&ima_ns->cleanup_list, &cleanup_list)) + queue_work(imans_wq, &ima_cleanup_work); }
static inline struct ima_namespace *to_ima_ns(struct ns_common *ns) @@ -168,8 +226,32 @@ static void imans_put(struct ns_common *ns) put_ima_ns(to_ima_ns(ns)); }
+static int imans_activate(struct ima_namespace *ima_ns) +{ + if (ima_ns == &init_ima_ns) + return 0; + + if (ima_ns->frozen) + return 0; + + mutex_lock(&frozen_lock); + if (ima_ns->frozen) + goto out; + + ima_ns->frozen = true; + + down_write(&ima_ns_list_lock); + list_add_tail(&ima_ns->list, &ima_ns_list); + up_write(&ima_ns_list_lock); +out: + mutex_unlock(&frozen_lock); + + return 0; +} + static int imans_install(struct nsset *nsset, struct ns_common *new) { + int res; struct nsproxy *nsproxy = nsset->nsproxy; struct ima_namespace *ns = to_ima_ns(new);
@@ -180,6 +262,10 @@ static int imans_install(struct nsset *nsset, struct ns_common *new) !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN)) return -EPERM;
+ res = imans_activate(ns); + if (res) + return res; + get_ima_ns(ns); put_ima_ns(nsproxy->ima_ns); nsproxy->ima_ns = ns; @@ -188,11 +274,12 @@ static int imans_install(struct nsset *nsset, struct ns_common *new) put_ima_ns(nsproxy->ima_ns_for_children); nsproxy->ima_ns_for_children = ns;
- return 0; + return res; }
int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) { + int res; struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns; struct ima_namespace *ns = to_ima_ns(nsc);
@@ -200,11 +287,15 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) if (nsproxy->ima_ns == nsproxy->ima_ns_for_children) return 0;
+ res = imans_activate(ns); + if (res) + return res; + get_ima_ns(ns); put_ima_ns(nsproxy->ima_ns); nsproxy->ima_ns = ns;
- return 0; + return res; }
static struct user_namespace *imans_owner(struct ns_common *ns)