diff --git a/include/linux/ima.h b/include/linux/ima.h index cfdd1280daff807646be2062aaf58a05622502fc..91c637c943ed218f1f05cf90cc3bdbc4ca77c948 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -246,7 +246,8 @@ struct ima_namespace *copy_ima_ns(unsigned long flags, void free_ima_ns(struct kref *kref); -int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk); +int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, + struct user_namespace *user_ns); static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns) { @@ -269,7 +270,8 @@ static inline struct ima_namespace *copy_ima_ns(unsigned long flags, } static inline int imans_on_fork(struct nsproxy *nsproxy, - struct task_struct *tsk) + struct task_struct *tsk, + struct user_namespace *user_ns) { return 0; } diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index e2cddc22dc53b2daeed34aad991022e495e58052..ff138b24b25db4387b68ab5e328a01cadf7a4958 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -204,7 +204,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) return ret; } - ret = imans_on_fork(new_ns, tsk); + ret = imans_on_fork(new_ns, tsk, user_ns); if (ret) { free_nsproxy(new_ns); return ret; diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 26c9bcd5ff7404a602fd1aeb9994c54e66c8087d..ac000920d486fd6a1b740599866e33a7418ec04b 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -93,6 +93,24 @@ static void ima_set_ns_policy(struct ima_namespace *ima_ns, ima_init_ns_policy(ima_ns, &setup_data); } +static int ima_swap_user_ns(struct ima_namespace *ima_ns, + struct user_namespace *user_ns) +{ + struct ucounts *ucounts; + + dec_ima_namespaces(ima_ns->ucounts); + put_user_ns(ima_ns->user_ns); + + ucounts = inc_ima_namespaces(user_ns); + if (!ucounts) + return -ENOSPC; + + ima_ns->user_ns = get_user_ns(user_ns); + ima_ns->ucounts = ucounts; + + return 0; +} + /** * Clone a new ns copying an original ima namespace, setting refcount to 1 * @@ -352,23 +370,33 @@ static int imans_install(struct nsset *nsset, struct ns_common *new) return res; } -int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) +int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, + struct user_namespace *user_ns) { int res; - struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns; - struct ima_namespace *ns = to_ima_ns(nsc); + struct ima_namespace *ima_ns = nsproxy->ima_ns_for_children; /* create_new_namespaces() already incremented the ref counter */ - if (nsproxy->ima_ns == nsproxy->ima_ns_for_children) + if (nsproxy->ima_ns == ima_ns) return 0; - res = imans_activate(ns); + /* It's possible that the user first unshares the IMA namespace and + * then creates a new user namespace on clone3(). In that case swap + * user namespace for the "current" one. + */ + if (ima_ns->user_ns != user_ns) { + res = ima_swap_user_ns(ima_ns, user_ns); + if (res) + return res; + } + + res = imans_activate(ima_ns); if (res) return res; - get_ima_ns(ns); + get_ima_ns(ima_ns); put_ima_ns(nsproxy->ima_ns); - nsproxy->ima_ns = ns; + nsproxy->ima_ns = ima_ns; return res; }