diff --git a/include/linux/ima.h b/include/linux/ima.h index 67af79961e4ebbb0ffd4e6044c3fc0b2030b0de8..77e7f512f61b1ac773e383f3f15226e75730d628 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -11,10 +11,12 @@ #include #include #include -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 dc536da8058e805393ce2ece3eb2d89aed7f5908..da7e732b7e800fb65ccd65ef7ef6f8e9fa9c8c79 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 b1c341e239a83589a23bd49d09ea0fa3fbba12c4..2238fb21eaf09c5378997d6bdbe862033dce7eb1 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 f3242727634aa2a46b00a1785ddadbe4a3df5352..fbec552886c1f70ab00c49fd08261e36245152c6 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 8f5f301406a22082eea971bf01237f8c03a8dc35..3a98cd536d05a4dfb4849f767aa128fdae14b908 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -21,9 +21,20 @@ #include #include #include +#include +#include +#include +#include +#include #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)