diff mbox series

[v7,01/14] ima: Add IMA namespace support

Message ID 20211216054323.1707384-2-stefanb@linux.vnet.ibm.com (mailing list archive)
State New, archived
Headers show
Series ima: Namespace IMA with audit support in IMA-ns | expand

Commit Message

Stefan Berger Dec. 16, 2021, 5:43 a.m. UTC
From: Stefan Berger <stefanb@linux.ibm.com>

Implement an IMA namespace data structure that gets created alongside a
user namespace with CLONE_NEWUSER. This lays down the foundation for
namespacing the different aspects of IMA (eg. IMA-audit, IMA-measurement,
IMA-appraisal).

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Suggested-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 include/linux/ima.h                      | 33 ++++++++++++
 include/linux/user_namespace.h           |  4 ++
 init/Kconfig                             | 10 ++++
 kernel/user.c                            |  7 +++
 kernel/user_namespace.c                  |  8 +++
 security/integrity/ima/Makefile          |  3 +-
 security/integrity/ima/ima.h             |  8 +++
 security/integrity/ima/ima_init.c        |  4 ++
 security/integrity/ima/ima_init_ima_ns.c | 28 ++++++++++
 security/integrity/ima/ima_ns.c          | 65 ++++++++++++++++++++++++
 10 files changed, 169 insertions(+), 1 deletion(-)
 create mode 100644 security/integrity/ima/ima_init_ima_ns.c
 create mode 100644 security/integrity/ima/ima_ns.c

Comments

Christian Brauner Dec. 16, 2021, 2:08 p.m. UTC | #1
On Thu, Dec 16, 2021 at 12:43:10AM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Implement an IMA namespace data structure that gets created alongside a
> user namespace with CLONE_NEWUSER. This lays down the foundation for
> namespacing the different aspects of IMA (eg. IMA-audit, IMA-measurement,
> IMA-appraisal).
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Suggested-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> ---
>  include/linux/ima.h                      | 33 ++++++++++++
>  include/linux/user_namespace.h           |  4 ++
>  init/Kconfig                             | 10 ++++
>  kernel/user.c                            |  7 +++
>  kernel/user_namespace.c                  |  8 +++
>  security/integrity/ima/Makefile          |  3 +-
>  security/integrity/ima/ima.h             |  8 +++
>  security/integrity/ima/ima_init.c        |  4 ++
>  security/integrity/ima/ima_init_ima_ns.c | 28 ++++++++++
>  security/integrity/ima/ima_ns.c          | 65 ++++++++++++++++++++++++
>  10 files changed, 169 insertions(+), 1 deletion(-)
>  create mode 100644 security/integrity/ima/ima_init_ima_ns.c
>  create mode 100644 security/integrity/ima/ima_ns.c
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index b6ab66a546ae..61461ee5b208 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -11,6 +11,7 @@
>  #include <linux/fs.h>
>  #include <linux/security.h>
>  #include <linux/kexec.h>
> +#include <linux/user_namespace.h>
>  #include <crypto/hash_info.h>
>  struct linux_binprm;
>  
> @@ -210,6 +211,38 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
>  }
>  #endif /* CONFIG_IMA_APPRAISE */
>  
> +extern struct ima_namespace init_ima_ns;
> +
> +#ifdef CONFIG_IMA_NS
> +
> +void free_ima_ns(struct user_namespace *ns);
> +int create_ima_ns(struct user_namespace *user_ns);
> +
> +static inline struct ima_namespace *get_current_ns(void)
> +{
> +	return current_user_ns()->ima_ns;
> +}
> +
> +#else
> +
> +static inline void free_ima_ns(struct user_namespace *user_ns)
> +{
> +}
> +
> +static inline int create_ima_ns(struct user_namespace *user_ns)
> +{
> +#ifdef CONFIG_IMA
> +	user_ns->ima_ns = &init_ima_ns;
> +#endif
> +	return 0;
> +}
> +
> +static inline struct ima_namespace *get_current_ns(void)
> +{
> +	return &init_ima_ns;
> +}
> +#endif /* CONFIG_IMA_NS */
> +
>  #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
>  extern bool ima_appraise_signature(enum kernel_read_file_id func);
>  #else
> diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
> index 33a4240e6a6f..5249db04d62b 100644
> --- a/include/linux/user_namespace.h
> +++ b/include/linux/user_namespace.h
> @@ -36,6 +36,7 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
>  #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
>  
>  struct ucounts;
> +struct ima_namespace;
>  
>  enum ucount_type {
>  	UCOUNT_USER_NAMESPACES,
> @@ -99,6 +100,9 @@ struct user_namespace {
>  #endif
>  	struct ucounts		*ucounts;
>  	long ucount_max[UCOUNT_COUNTS];
> +#ifdef CONFIG_IMA
> +	struct ima_namespace	*ima_ns;
> +#endif
>  } __randomize_layout;
>  
>  struct ucounts {
> diff --git a/init/Kconfig b/init/Kconfig
> index 11f8a845f259..27890607e8cb 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1242,6 +1242,16 @@ config NET_NS
>  	  Allow user space to create what appear to be multiple instances
>  	  of the network stack.
>  
> +config IMA_NS
> +	bool "IMA namespace"
> +	depends on USER_NS
> +	depends on IMA
> +	default y
> +	help
> +	  Allow the creation of IMA namespaces for each user namespace.
> +	  Namespaced IMA enables having IMA features work separately
> +	  in each IMA namespace.
> +
>  endif # NAMESPACES
>  
>  config CHECKPOINT_RESTORE
> diff --git a/kernel/user.c b/kernel/user.c
> index e2cf8c22b539..287751d89b44 100644
> --- a/kernel/user.c
> +++ b/kernel/user.c
> @@ -20,6 +20,10 @@
>  #include <linux/user_namespace.h>
>  #include <linux/proc_ns.h>
>  
> +#ifdef CONFIG_IMA
> +extern struct ima_namespace init_ima_ns;
> +#endif
> +
>  /*
>   * userns count is 1 for root user, 1 for init_uts_ns,
>   * and 1 for... ?
> @@ -67,6 +71,9 @@ struct user_namespace init_user_ns = {
>  	.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
>  	.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
>  #endif
> +#ifdef CONFIG_IMA
> +	.ima_ns = &init_ima_ns,
> +#endif
>  };
>  EXPORT_SYMBOL_GPL(init_user_ns);
>  
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 6b2e3ca7ee99..6fa01323aac9 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -20,6 +20,7 @@
>  #include <linux/fs_struct.h>
>  #include <linux/bsearch.h>
>  #include <linux/sort.h>
> +#include <linux/ima.h>
>  
>  static struct kmem_cache *user_ns_cachep __read_mostly;
>  static DEFINE_MUTEX(userns_state_mutex);
> @@ -141,8 +142,14 @@ int create_user_ns(struct cred *new)
>  	if (!setup_userns_sysctls(ns))
>  		goto fail_keyring;
>  
> +	ret = create_ima_ns(ns);

Instead of greedily allocating a new ima namespace for each new user
namespace creation and wasting memory that is likely wasted since most
containers won't use ima (for a long time at least) have you considered
lazily allocating it like I suggested in one of my first reviews?

So under the assumption that the only way for a container to get its own
ima policy it needs to have mounted a new securityfs instance you can
move the ima namespace allocation into fill_super/ima_fs_ns_init():

From 46fd4f19e1360bee167fccb11e793a3a3331ccc2 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Thu, 16 Dec 2021 14:57:30 +0100
Subject: [PATCH] !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!

Lazily initialize ima_ns. This avoids pointlessly wasting memory that is never
needed or used which I think will be the case for most containers.
---
 include/linux/ima.h               |  2 +-
 kernel/user_namespace.c           |  6 ------
 security/integrity/ima/ima_fs.c   | 20 ++++++++++++++++++--
 security/integrity/ima/ima_main.c |  5 ++++-
 security/integrity/ima/ima_ns.c   |  7 ++++++-
 5 files changed, 29 insertions(+), 11 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index a2705aa5242a..cb1b94df11a1 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -230,7 +230,7 @@ int create_ima_ns(struct user_namespace *user_ns);
 
 static inline struct ima_namespace *get_current_ns(void)
 {
-	return current_user_ns()->ima_ns;
+	return smp_load_acquire(&current_user_ns()->ima_ns);
 }
 
 static inline int ima_securityfs_init(struct user_namespace *user_ns,
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6fa01323aac9..653f8fa83b69 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -142,14 +142,8 @@ int create_user_ns(struct cred *new)
 	if (!setup_userns_sysctls(ns))
 		goto fail_keyring;
 
-	ret = create_ima_ns(ns);
-	if (ret)
-		goto fail_sysctls;
-
 	set_cred_user_ns(new, ns);
 	return 0;
-fail_sysctls:
-	retire_userns_sysctls(ns);
 fail_keyring:
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 	key_put(ns->persistent_keyring_register);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3b8001ba62e3..971620a22dab 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -378,7 +378,7 @@ static const struct seq_operations ima_policy_seqops = {
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
 	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
-	struct ima_namespace *ns = user_ns->ima_ns;
+	struct ima_namespace *ns = user_ns->ima_ns; /* no need to use acquire semantics it's guaranteed to be initialized */
 
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
@@ -450,7 +450,8 @@ static const struct file_operations ima_measure_policy_ops = {
 
 int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 {
-	struct ima_namespace *ns = user_ns->ima_ns;
+	int ret;
+	struct ima_namespace *ns = user_ns->ima_ns; /* no need to use acquire semantics it's guaranteed to be initialized */
 	struct dentry *int_dir;
 	struct dentry *ima_dir = NULL;
 	struct dentry *ima_symlink = NULL;
@@ -459,6 +460,21 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 	struct dentry *runtime_measurements_count = NULL;
 	struct dentry *violations = NULL;
 
+	/*
+	 * While multiple superblocks can exist they are keyed by userns in
+	 * s_fs_info for securityfs. The first time a userns mounts a
+	 * securityfs instance we lazily allocate the ima_namespace for the
+	 * userns since that's the only way a userns can meaningfully use ima.
+	 * The vfs ensure we're the only one to call fill_super() and hence
+	 * ima_fs_ns_init() so we don't need any memory barriers here, i.e.
+	 * user_ns->ima_ns can't change while we're in here.
+	 */
+	if (!ns) {
+		ret =  create_ima_ns(user_ns);
+		if (ret)
+			return ret;
+	}
+
 	/* FIXME: update when evm and integrity are namespaced */
 	if (user_ns != &init_user_ns) {
 		int_dir =
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 4c85a8df3c86..a0e71416561d 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -414,7 +414,10 @@ static int process_measurement(struct ima_namespace *ns,
 	int ret = 0;
 
 	while (user_ns) {
-		ns = user_ns->ima_ns;
+		/* the container has not loaded a separate policy (yet) */
+		ns = smp_load_acquire(&user_ns->ima_ns);
+		if (!ns)
+			continue;
 
 		ret = __process_measurement(ns, file, cred, secid, buf, size,
 					    mask, func);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index d192a80c927f..5c7177b07344 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -31,7 +31,8 @@ int create_ima_ns(struct user_namespace *user_ns)
 	if (err)
 		goto fail_free;
 
-	user_ns->ima_ns = ns;
+	/* Pairs with smp_load_acquire() in get_current_ns() and process_measurement(). */
+	smp_store_release(&user_ns->ima_ns, ns);
 
 	return 0;
 
@@ -52,6 +53,10 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 
 void free_ima_ns(struct user_namespace *user_ns)
 {
+	/* No need to use acquire semantics as the userns can't be reached
+	 * anymore from userspace so either ima_ns has been initialized or it
+	 * never has.
+	 */
 	struct ima_namespace *ns = user_ns->ima_ns;
 
 	if (WARN_ON(ns == &init_ima_ns))
James Bottomley Dec. 16, 2021, 9:52 p.m. UTC | #2
On Thu, 2021-12-16 at 15:08 +0100, Christian Brauner wrote:
> On Thu, Dec 16, 2021 at 12:43:10AM -0500, Stefan Berger wrote:
[...]
> > diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> > index 6b2e3ca7ee99..6fa01323aac9 100644
> > --- a/kernel/user_namespace.c
> > +++ b/kernel/user_namespace.c
> > @@ -20,6 +20,7 @@
> >  #include <linux/fs_struct.h>
> >  #include <linux/bsearch.h>
> >  #include <linux/sort.h>
> > +#include <linux/ima.h>
> >  
> >  static struct kmem_cache *user_ns_cachep __read_mostly;
> >  static DEFINE_MUTEX(userns_state_mutex);
> > @@ -141,8 +142,14 @@ int create_user_ns(struct cred *new)
> >  	if (!setup_userns_sysctls(ns))
> >  		goto fail_keyring;
> >  
> > +	ret = create_ima_ns(ns);
> 
> Instead of greedily allocating a new ima namespace for each new user
> namespace creation and wasting memory that is likely wasted since
> most containers won't use ima (for a long time at least) have you
> considered lazily allocating it like I suggested in one of my first
> reviews?
> 
> So under the assumption that the only way for a container to get its
> own ima policy it needs to have mounted a new securityfs instance you
> can move the ima namespace allocation into
> fill_super/ima_fs_ns_init():

The current patch set has the ima namespace born with an empty policy,
meaning it can never do anything until a new policy is inserted via a
write to the securityfs, and therefore the IMA namespace could be
lazily allocated.  However, that's not quite how the initial IMA
namespace behaves because a policy can be passed in on the kernel
command line (or built into the kernel).  If the ima NS were born with
a default policy (say taken from the initial IMA default policy, or
simply inherited from the parent at creation time) then we wouldn't be
able to do lazy allocation.  Before we tie ourselves to never being
able to have a default policy for an IMA namespace, perhaps we should
discuss if this is the correct behaviour we want to nail into the
system.

James
Christian Brauner Dec. 17, 2021, 9:55 a.m. UTC | #3
On Thu, Dec 16, 2021 at 04:52:47PM -0500, James Bottomley wrote:
> On Thu, 2021-12-16 at 15:08 +0100, Christian Brauner wrote:
> > On Thu, Dec 16, 2021 at 12:43:10AM -0500, Stefan Berger wrote:
> [...]
> > > diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> > > index 6b2e3ca7ee99..6fa01323aac9 100644
> > > --- a/kernel/user_namespace.c
> > > +++ b/kernel/user_namespace.c
> > > @@ -20,6 +20,7 @@
> > >  #include <linux/fs_struct.h>
> > >  #include <linux/bsearch.h>
> > >  #include <linux/sort.h>
> > > +#include <linux/ima.h>
> > >  
> > >  static struct kmem_cache *user_ns_cachep __read_mostly;
> > >  static DEFINE_MUTEX(userns_state_mutex);
> > > @@ -141,8 +142,14 @@ int create_user_ns(struct cred *new)
> > >  	if (!setup_userns_sysctls(ns))
> > >  		goto fail_keyring;
> > >  
> > > +	ret = create_ima_ns(ns);
> > 
> > Instead of greedily allocating a new ima namespace for each new user
> > namespace creation and wasting memory that is likely wasted since
> > most containers won't use ima (for a long time at least) have you
> > considered lazily allocating it like I suggested in one of my first
> > reviews?
> > 
> > So under the assumption that the only way for a container to get its
> > own ima policy it needs to have mounted a new securityfs instance you
> > can move the ima namespace allocation into
> > fill_super/ima_fs_ns_init():
> 
> The current patch set has the ima namespace born with an empty policy,
> meaning it can never do anything until a new policy is inserted via a
> write to the securityfs, and therefore the IMA namespace could be
> lazily allocated.  However, that's not quite how the initial IMA
> namespace behaves because a policy can be passed in on the kernel
> command line (or built into the kernel).  If the ima NS were born with
> a default policy (say taken from the initial IMA default policy, or
> simply inherited from the parent at creation time) then we wouldn't be
> able to do lazy allocation.  Before we tie ourselves to never being
> able to have a default policy for an IMA namespace, perhaps we should
> discuss if this is the correct behaviour we want to nail into the
> system.

If ima in the future decides to do policy inheritance it can simply
switch from delayed allocation at mount time to allocation at userns
creation time. So we can proceed with lazy allocation without much
problem for now.

In addition what is happening now is in effect policy inheritance, i.e.
each container is bound by the parent ima_ns policy until it decides to
setup its own.

Aside from that the container manager can and should be responsible for
the default ima policy to apply to containers. The default ima policy
can be passed through the spec file, configuration file, or - for the
hardcore people - compiled into the container manager itself. This is
not different from LSMs (e.g. AppArmor, seccomp) where the policy for
each container is generated from a fixed template that was built into
the container manager binary and then written via
/proc/<pid>/attr/current during container setup.

During container setup the process that ultimately calls exec to execute
the payload does all of the required setup work. The setup process
should not be automatically bound by a default policy that gets created
when the userns is created. That will just cause problems during
container setup.
Until the setup process has decided that all preliminary setup steps are
done only the ancestor policy should restrict it.
This is exactly the same for all LSMs and seccomp. They all are usually
setup closely before calling exec. I see no reason for ima to diverge
from this model.
diff mbox series

Patch

diff --git a/include/linux/ima.h b/include/linux/ima.h
index b6ab66a546ae..61461ee5b208 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -11,6 +11,7 @@ 
 #include <linux/fs.h>
 #include <linux/security.h>
 #include <linux/kexec.h>
+#include <linux/user_namespace.h>
 #include <crypto/hash_info.h>
 struct linux_binprm;
 
@@ -210,6 +211,38 @@  static inline int ima_inode_removexattr(struct dentry *dentry,
 }
 #endif /* CONFIG_IMA_APPRAISE */
 
+extern struct ima_namespace init_ima_ns;
+
+#ifdef CONFIG_IMA_NS
+
+void free_ima_ns(struct user_namespace *ns);
+int create_ima_ns(struct user_namespace *user_ns);
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return current_user_ns()->ima_ns;
+}
+
+#else
+
+static inline void free_ima_ns(struct user_namespace *user_ns)
+{
+}
+
+static inline int create_ima_ns(struct user_namespace *user_ns)
+{
+#ifdef CONFIG_IMA
+	user_ns->ima_ns = &init_ima_ns;
+#endif
+	return 0;
+}
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return &init_ima_ns;
+}
+#endif /* CONFIG_IMA_NS */
+
 #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
 extern bool ima_appraise_signature(enum kernel_read_file_id func);
 #else
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 33a4240e6a6f..5249db04d62b 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -36,6 +36,7 @@  struct uid_gid_map { /* 64 bytes -- 1 cache line */
 #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
 
 struct ucounts;
+struct ima_namespace;
 
 enum ucount_type {
 	UCOUNT_USER_NAMESPACES,
@@ -99,6 +100,9 @@  struct user_namespace {
 #endif
 	struct ucounts		*ucounts;
 	long ucount_max[UCOUNT_COUNTS];
+#ifdef CONFIG_IMA
+	struct ima_namespace	*ima_ns;
+#endif
 } __randomize_layout;
 
 struct ucounts {
diff --git a/init/Kconfig b/init/Kconfig
index 11f8a845f259..27890607e8cb 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1242,6 +1242,16 @@  config NET_NS
 	  Allow user space to create what appear to be multiple instances
 	  of the network stack.
 
+config IMA_NS
+	bool "IMA namespace"
+	depends on USER_NS
+	depends on IMA
+	default y
+	help
+	  Allow the creation of IMA namespaces for each user namespace.
+	  Namespaced IMA enables having IMA features work separately
+	  in each IMA namespace.
+
 endif # NAMESPACES
 
 config CHECKPOINT_RESTORE
diff --git a/kernel/user.c b/kernel/user.c
index e2cf8c22b539..287751d89b44 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -20,6 +20,10 @@ 
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
 
+#ifdef CONFIG_IMA
+extern struct ima_namespace init_ima_ns;
+#endif
+
 /*
  * userns count is 1 for root user, 1 for init_uts_ns,
  * and 1 for... ?
@@ -67,6 +71,9 @@  struct user_namespace init_user_ns = {
 	.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
 	.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
 #endif
+#ifdef CONFIG_IMA
+	.ima_ns = &init_ima_ns,
+#endif
 };
 EXPORT_SYMBOL_GPL(init_user_ns);
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6b2e3ca7ee99..6fa01323aac9 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -20,6 +20,7 @@ 
 #include <linux/fs_struct.h>
 #include <linux/bsearch.h>
 #include <linux/sort.h>
+#include <linux/ima.h>
 
 static struct kmem_cache *user_ns_cachep __read_mostly;
 static DEFINE_MUTEX(userns_state_mutex);
@@ -141,8 +142,14 @@  int create_user_ns(struct cred *new)
 	if (!setup_userns_sysctls(ns))
 		goto fail_keyring;
 
+	ret = create_ima_ns(ns);
+	if (ret)
+		goto fail_sysctls;
+
 	set_cred_user_ns(new, ns);
 	return 0;
+fail_sysctls:
+	retire_userns_sysctls(ns);
 fail_keyring:
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 	key_put(ns->persistent_keyring_register);
@@ -196,6 +203,7 @@  static void free_user_ns(struct work_struct *work)
 			kfree(ns->projid_map.forward);
 			kfree(ns->projid_map.reverse);
 		}
+		free_ima_ns(ns);
 		retire_userns_sysctls(ns);
 		key_free_user_ns(ns);
 		ns_free_inum(&ns->ns);
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 2499f2485c04..b86a35fbed60 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -7,13 +7,14 @@ 
 obj-$(CONFIG_IMA) += ima.o
 
 ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
-	 ima_policy.o ima_template.o ima_template_lib.o
+	 ima_policy.o ima_template.o ima_template_lib.o ima_init_ima_ns.o
 ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
 ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
 ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
 ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
 ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
 ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
+ima-$(CONFIG_IMA_NS) += ima_ns.o
 
 ifeq ($(CONFIG_EFI),y)
 ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index be965a8715e4..4606d3b1493f 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -119,6 +119,10 @@  struct ima_kexec_hdr {
 	u64 count;
 };
 
+struct ima_namespace {
+	int avoid_zero_size;
+} __randomize_layout;
+
 extern const int read_idmap[];
 
 #ifdef CONFIG_HAVE_IMA_KEXEC
@@ -418,6 +422,10 @@  static inline void ima_free_modsig(struct modsig *modsig)
 }
 #endif /* CONFIG_IMA_APPRAISE_MODSIG */
 
+int ima_ns_init(void);
+struct ima_namespace;
+int ima_init_namespace(struct ima_namespace *ns);
+
 /* LSM based policy rules require audit */
 #ifdef CONFIG_IMA_LSM_RULES
 
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b26fa67476b4..f6ae4557a0da 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -120,6 +120,10 @@  int __init ima_init(void)
 {
 	int rc;
 
+	rc = ima_ns_init();
+	if (rc)
+		return rc;
+
 	ima_tpm_chip = tpm_default_chip();
 	if (!ima_tpm_chip)
 		pr_info("No TPM chip found, activating TPM-bypass!\n");
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
new file mode 100644
index 000000000000..46d8cb2113a1
--- /dev/null
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -0,0 +1,28 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ *   Yuqiong Sun <suny@us.ibm.com>
+ *   Stefan Berger <stefanb@linux.vnet.ibm.com>
+ */
+
+#include <linux/export.h>
+#include <linux/user_namespace.h>
+#include <linux/proc_ns.h>
+#include <linux/ima.h>
+
+#include "ima.h"
+
+int ima_init_namespace(struct ima_namespace *ns)
+{
+	return 0;
+}
+
+int __init ima_ns_init(void)
+{
+	return ima_init_namespace(&init_ima_ns);
+}
+
+struct ima_namespace init_ima_ns = {
+};
+EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..bc9a2c11d70a
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,65 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ *  Yuqiong Sun <suny@us.ibm.com>
+ *  Stefan Berger <stefanb@linux.vnet.ibm.com>
+ */
+
+#include <linux/kref.h>
+#include <linux/slab.h>
+#include <linux/ima.h>
+#include <linux/mount.h>
+#include <linux/proc_ns.h>
+#include <linux/lsm_hooks.h>
+
+#include "ima.h"
+
+static struct kmem_cache *imans_cachep;
+
+int create_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns;
+	int err;
+
+	ns = kmem_cache_zalloc(imans_cachep, GFP_KERNEL);
+	if (!ns)
+		return -ENOMEM;
+	pr_debug("NEW     ima_ns: 0x%p\n", ns);
+
+	err = ima_init_namespace(ns);
+	if (err)
+		goto fail_free;
+
+	user_ns->ima_ns = ns;
+
+	return 0;
+
+fail_free:
+	kmem_cache_free(imans_cachep, ns);
+
+	return err;
+}
+
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+	pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+	kmem_cache_free(imans_cachep, ns);
+}
+
+void free_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns = user_ns->ima_ns;
+
+	if (WARN_ON(ns == &init_ima_ns))
+		return;
+
+	destroy_ima_ns(ns);
+}
+
+static int __init imans_cache_init(void)
+{
+	imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
+	return 0;
+}
+subsys_initcall(imans_cache_init)