Linux Security Modules development
 help / color / mirror / Atom feed
* [RFC PATCH 23/30] keys: Add domain tag to the keyring search criteria
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add keyring_search_tag() version of keyring_search(), that allows to
specify the key domain tag.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/key.h     | 17 +++++++++++++----
 security/keys/keyring.c | 15 +++++++++------
 2 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/include/linux/key.h b/include/linux/key.h
index 0f2e24f13c2b..223ab9d76d15 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -416,10 +416,11 @@ extern int restrict_link_reject(struct key *keyring,
 
 extern int keyring_clear(struct key *keyring);
 
-extern key_ref_t keyring_search(key_ref_t keyring,
-				struct key_type *type,
-				const char *description,
-				bool recurse);
+extern key_ref_t keyring_search_tag(key_ref_t keyring,
+				    struct key_type *type,
+				    const char *description,
+				    struct key_tag *domain_tag,
+				    bool recurse);
 
 extern int keyring_add_key(struct key *keyring,
 			   struct key *key);
@@ -429,6 +430,14 @@ extern int keyring_restrict(key_ref_t keyring, const char *type,
 
 extern struct key *key_lookup(key_serial_t id);
 
+static inline key_ref_t keyring_search(key_ref_t keyring,
+				       struct key_type *type,
+				       const char *description,
+				       bool recurse)
+{
+	return keyring_search_tag(keyring, type, description, NULL, recurse);
+}
+
 static inline key_serial_t key_serial(const struct key *key)
 {
 	return key ? key->serial : 0;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 14abfe765b7e..12583241ff63 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -925,22 +925,25 @@ key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
 }
 
 /**
- * keyring_search - Search the supplied keyring tree for a matching key
+ * keyring_search_tag - Search the supplied keyring tree for a matching key
  * @keyring: The root of the keyring tree to be searched.
  * @type: The type of keyring we want to find.
  * @description: The name of the keyring we want to find.
+ * @domain_tag: The domain_tag of the key we want to find.
  * @recurse: True to search the children of @keyring also
  *
  * As keyring_search_rcu() above, but using the current task's credentials and
  * type's default matching function and preferred search method.
  */
-key_ref_t keyring_search(key_ref_t keyring,
-			 struct key_type *type,
-			 const char *description,
-			 bool recurse)
+key_ref_t keyring_search_tag(key_ref_t keyring,
+			     struct key_type *type,
+			     const char *description,
+			     struct key_tag *domain_tag,
+			     bool recurse)
 {
 	struct keyring_search_context ctx = {
 		.index_key.type		= type,
+		.index_key.domain_tag   = domain_tag,
 		.index_key.description	= description,
 		.index_key.desc_len	= strlen(description),
 		.cred			= current_cred(),
@@ -968,7 +971,7 @@ key_ref_t keyring_search(key_ref_t keyring,
 		type->match_free(&ctx.match_data);
 	return key;
 }
-EXPORT_SYMBOL(keyring_search);
+EXPORT_SYMBOL(keyring_search_tag);
 
 static struct key_restriction *keyring_restriction_alloc(
 	key_restrict_link_func_t check)
-- 
2.20.1


^ permalink raw reply related

* Re: [RFC PATCH 00/30] ima: Introduce IMA namespace
From: Christian Brauner @ 2020-08-18 15:53 UTC (permalink / raw)
  To: krzysztof.struczynski
  Cc: linux-integrity, linux-kernel, containers, linux-security-module,
	zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

On Tue, Aug 18, 2020 at 05:20:07PM +0200, krzysztof.struczynski@huawei.com wrote:
> From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
> 
> IMA has not been designed to work with containers. It handles every
> process in the same way, and it cannot distinguish if a process belongs to
> a container or not.
> 
> Containers use namespaces to make it appear to the processes in the
> containers that they have their own isolated instance of the global
> resource. For IMA as well, it is desirable to let processes in the
> containers have IMA functionality independent from other containers:
> separate policy rules, measurement list, additional appraisal keys to
> verify the container image, separate audit logs.
> 
> As previous work done in this area, this patch series introduces the IMA
> namespace, which is a separate instance of IMA to handle a subset of
> processes that belong to a container.
> 
> The IMA namespace is created using clone3() or unshare() system calls. It
> is important to configure the namespace before any process appears in it,
> so that the new policy rules apply to the very first process in the
> namespace. To achieve that, the intermediate namespace ima_ns_for_children
> is used. It stores the configuration and becomes active on the next fork
> or when the first process enters it using the setns() system call. The
> similar process is used for the time namespace.
> 
> The IMA namespace can be configured using the new securityfs directory
> entries that allow the user to set the policy rules, x509 certificate for
> appraisal and pass IMA configuration parameters normally included in the
> kernel command line parameters. It is intended to extend the clone_args to
> allow configuration from clone3() syscall.

Not to be the downer right away but just as an fyi, if this patchset
makes it, clone3() will not allow to be extended with any real
second-level pointers. That will see a hard NAK from me and several
other maintainers.

Christian

^ permalink raw reply

* [RFC PATCH 25/30] keys: Allow to set key domain tag separately from the key type
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add KEY_ALLOC_DOMAIN_* flags so that the key domain tag can be
specified on the key creation. This is done to separate the
key domain setting from the key type.

If applied to the keyring, it will set the requested domain tag for
every key added to that keyring.

IMA uses the existing key_type_asymmetric for appraisal, but also has
to specify the key domain to bind appraisal key with the ima namespace.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/key.h | 10 ++++++++++
 security/keys/key.c | 16 ++++++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/include/linux/key.h b/include/linux/key.h
index 223ab9d76d15..47430cd7fbc6 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -272,6 +272,12 @@ struct key {
 	 * restriction.
 	 */
 	struct key_restriction *restrict_link;
+
+	/* This is set on a keyring to indicate that every key added to this
+	 * keyring should be tagged with a given key domain tag. It is ignored
+	 * for the non-keyring keys and can be overridden by the key-type flags.
+	 */
+	unsigned long key_alloc_domain;
 };
 
 extern struct key *key_alloc(struct key_type *type,
@@ -290,6 +296,10 @@ extern struct key *key_alloc(struct key_type *type,
 #define KEY_ALLOC_BYPASS_RESTRICTION	0x0008	/* Override the check on restricted keyrings */
 #define KEY_ALLOC_UID_KEYRING		0x0010	/* allocating a user or user session keyring */
 
+/* Only one domain can be set */
+#define KEY_ALLOC_DOMAIN_IMA		0x0100  /* add IMA domain tag, based on the "current" */
+#define KEY_ALLOC_DOMAIN_MASK		0xFF00
+
 extern void key_revoke(struct key *key);
 extern void key_invalidate(struct key *key);
 extern void key_put(struct key *key);
diff --git a/security/keys/key.c b/security/keys/key.c
index e282c6179b21..1b0183d33bbc 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -278,6 +278,19 @@ struct key *key_alloc(struct key_type *type, const char *desc,
 	if (!key)
 		goto no_memory_2;
 
+	if (flags & KEY_ALLOC_DOMAIN_MASK) {
+		/* set alloc domain for all keys added to this keyring */
+		if (type == &key_type_keyring)
+			key->key_alloc_domain = (flags & KEY_ALLOC_DOMAIN_MASK);
+
+		/* set domain tag if it's not predefined for the key type */
+		if ((!type->flags) && (flags & KEY_ALLOC_DOMAIN_IMA))
+			/* Set it to something meaningful after adding a key
+			 * domain to the ima namespace.
+			 */
+			key->index_key.domain_tag = NULL;
+	}
+
 	key->index_key.desc_len = desclen;
 	key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL);
 	if (!key->index_key.description)
@@ -925,6 +938,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref,
 			perm |= KEY_POS_WRITE;
 	}
 
+	if (keyring->key_alloc_domain)
+		flags |= keyring->key_alloc_domain;
+
 	/* allocate a new key */
 	key = key_alloc(index_key.type, index_key.description,
 			cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 28/30] ima: Load per ima namespace x509 certificate
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

If configured, load the x509 certificate when the first process is
born into the new ima namespace. User can set the path to the
certificate by writing to the x509_for_children entry in the ima
securityfs.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/digsig.c     |  6 +--
 security/integrity/ima/ima_ns.c | 69 ++++++++++++++++++++++++++++-----
 security/integrity/integrity.h  |  2 +-
 3 files changed, 63 insertions(+), 14 deletions(-)

diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index 523fc786c4d7..8cd54bc83892 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -170,8 +170,8 @@ int __init integrity_init_keyring(const unsigned int id)
 	return __integrity_init_keyring(id, perm, restriction);
 }
 
-int __init integrity_add_key(const unsigned int id, const void *data,
-			     off_t size, key_perm_t perm)
+int integrity_add_key(const unsigned int id, const void *data,
+		      off_t size, key_perm_t perm)
 {
 	key_ref_t key;
 	int rc = 0;
@@ -195,7 +195,7 @@ int __init integrity_add_key(const unsigned int id, const void *data,
 
 }
 
-int __init integrity_load_x509(const unsigned int id, const char *path)
+int integrity_load_x509(const unsigned int id, const char *path)
 {
 	void *data;
 	loff_t size;
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 872dc6a96a96..11e1d896f603 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -48,6 +48,30 @@ static void dec_ima_namespaces(struct ucounts *ucounts)
 	return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
 }
 
+#ifdef CONFIG_IMA_LOAD_X509
+static int ima_ns_load_x509(struct ima_namespace *ima_ns)
+{
+	int res = 0;
+	int unset_flags =
+		ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE;
+
+	if (!ima_ns->x509_path_for_children)
+		return res;
+
+	ima_ns->policy_data->ima_policy_flag &= ~unset_flags;
+	res = integrity_load_x509(INTEGRITY_KEYRING_IMA,
+				  ima_ns->x509_path_for_children);
+	ima_ns->policy_data->ima_policy_flag |= unset_flags;
+
+	return res;
+}
+#else
+static inline int ima_ns_load_x509(struct ima_namespace *ima_ns)
+{
+	return 0;
+}
+#endif
+
 static struct ima_namespace *ima_ns_alloc(void)
 {
 	struct ima_namespace *ima_ns;
@@ -365,6 +389,10 @@ static int imans_activate(struct ima_namespace *ima_ns)
 	if (res < 0)
 		goto out;
 
+	res = ima_ns_load_x509(ima_ns);
+	if (res < 0)
+		goto out;
+
 	ima_ns->frozen = true;
 
 	/* Set current last element as list's head */
@@ -388,6 +416,7 @@ 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);
+	struct ima_namespace *old_ns = nsproxy->ima_ns;
 
 	if (!current_is_single_threaded())
 		return -EUSERS;
@@ -396,14 +425,24 @@ 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);
+	put_ima_ns(old_ns);
 	nsproxy->ima_ns = ns;
 
+	/* The activation has to take place, after attaching the new namespace
+	 * to the nsproxy. This is because one part of the activation process,
+	 * is loading the appraisal keys, which temporary disables appraisal in
+	 * the current ima namespace, and it must not happen for the "old" ima
+	 * namespace.
+	 */
+	res = imans_activate(ns);
+	if (res) {
+		get_ima_ns(old_ns);
+		put_ima_ns(ns);
+		nsproxy->ima_ns = old_ns;
+		return res;
+	}
+
 	get_ima_ns(ns);
 	put_ima_ns(nsproxy->ima_ns_for_children);
 	nsproxy->ima_ns_for_children = ns;
@@ -416,6 +455,7 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk,
 {
 	int res;
 	struct ima_namespace *ima_ns = nsproxy->ima_ns_for_children;
+	struct ima_namespace *old_ima_ns = nsproxy->ima_ns;
 
 	/* create_new_namespaces() already incremented the ref counter */
 	if (nsproxy->ima_ns == ima_ns)
@@ -431,14 +471,23 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk,
 			return res;
 	}
 
-	res = imans_activate(ima_ns);
-	if (res)
-		return res;
-
 	get_ima_ns(ima_ns);
-	put_ima_ns(nsproxy->ima_ns);
+	put_ima_ns(old_ima_ns);
 	nsproxy->ima_ns = ima_ns;
 
+	/* The activation has to take place, after attaching the new namespace
+	 * to the nsproxy. This is because one part of the activation process,
+	 * is loading the appraisal keys, which temporary disables appraisal in
+	 * the current ima namespace, and it must not happen for the "old" ima
+	 * namespace.
+	 */
+	res = imans_activate(ima_ns);
+	if (res) {
+		get_ima_ns(old_ima_ns);
+		put_ima_ns(ima_ns);
+		nsproxy->ima_ns = old_ima_ns;
+	}
+
 	return res;
 }
 
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 207a1aef28e4..9b080b9fe242 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -179,7 +179,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
 int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);
 
 int __init integrity_init_keyring(const unsigned int id);
-int __init integrity_load_x509(const unsigned int id, const char *path);
+int integrity_load_x509(const unsigned int id, const char *path);
 int __init integrity_load_cert(const unsigned int id, const char *source,
 			       const void *data, size_t len, key_perm_t perm);
 #else
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 27/30] integrity: Add key domain tag to the search criteria
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add key domain tag to the search criteria in digsig module. If the
domain tag is not set for the keys from the given keyring, it is set to
NULL and the behaviour is unchanged.

The key domain tag is added to the ima appraisal keys loaded to the
system ima keyring.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/digsig.h                 | 11 ++++---
 lib/digsig.c                           | 11 +++----
 security/integrity/digsig.c            | 40 ++++++++++++++++++++++----
 security/integrity/digsig_asymmetric.c | 20 ++++++++-----
 security/integrity/integrity.h         | 11 ++++---
 5 files changed, 67 insertions(+), 26 deletions(-)

diff --git a/include/linux/digsig.h b/include/linux/digsig.h
index 2ace69e41088..9e4121253899 100644
--- a/include/linux/digsig.h
+++ b/include/linux/digsig.h
@@ -44,13 +44,16 @@ struct signature_hdr {
 
 #if defined(CONFIG_SIGNATURE) || defined(CONFIG_SIGNATURE_MODULE)
 
-int digsig_verify(struct key *keyring, const char *sig, int siglen,
-					const char *digest, int digestlen);
+int digsig_verify(struct key *keyring, struct key_tag *domain_tag,
+		  const char *sig, int siglen, const char *digest,
+		  int digestlen);
 
 #else
 
-static inline int digsig_verify(struct key *keyring, const char *sig,
-				int siglen, const char *digest, int digestlen)
+static inline int digsig_verify(struct key *keyring,
+				struct key_tag *domain_tag,
+				const char *sig, int siglen, const char *digest,
+				int digestlen)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/lib/digsig.c b/lib/digsig.c
index e0627c3e53b2..bd234b1abb4f 100644
--- a/lib/digsig.c
+++ b/lib/digsig.c
@@ -196,8 +196,8 @@ static int digsig_verify_rsa(struct key *key,
  * Normally hash of the content is used as a data for this function.
  *
  */
-int digsig_verify(struct key *keyring, const char *sig, int siglen,
-						const char *data, int datalen)
+int digsig_verify(struct key *keyring, struct key_tag *domain_tag,
+		  const char *sig, int siglen, const char *data, int datalen)
 {
 	int err = -ENOMEM;
 	struct signature_hdr *sh = (struct signature_hdr *)sig;
@@ -217,14 +217,15 @@ int digsig_verify(struct key *keyring, const char *sig, int siglen,
 	if (keyring) {
 		/* search in specific keyring */
 		key_ref_t kref;
-		kref = keyring_search(make_key_ref(keyring, 1UL),
-				      &key_type_user, name, true);
+		kref = keyring_search_tag(make_key_ref(keyring, 1UL),
+					  &key_type_user, name,
+					  domain_tag, true);
 		if (IS_ERR(kref))
 			key = ERR_CAST(kref);
 		else
 			key = key_ref_to_ptr(kref);
 	} else {
-		key = request_key(&key_type_user, name, NULL);
+		key = request_key_tag(&key_type_user, name, domain_tag, NULL);
 	}
 	if (IS_ERR(key)) {
 		pr_err("key not found, id: %s\n", name);
diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c
index e9cbadade74b..523fc786c4d7 100644
--- a/security/integrity/digsig.c
+++ b/security/integrity/digsig.c
@@ -15,6 +15,7 @@
 #include <linux/vmalloc.h>
 #include <crypto/public_key.h>
 #include <keys/system_keyring.h>
+#include <linux/ima.h>
 
 #include "integrity.h"
 
@@ -31,6 +32,16 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = {
 	".platform",
 };
 
+static unsigned long keyring_alloc_flags[INTEGRITY_KEYRING_MAX] = {
+	KEY_ALLOC_NOT_IN_QUOTA,
+#ifdef CONFIG_IMA_NS
+	KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_DOMAIN_IMA,
+#else
+	KEY_ALLOC_NOT_IN_QUOTA,
+#endif
+	KEY_ALLOC_NOT_IN_QUOTA,
+};
+
 #ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY
 #define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted
 #else
@@ -56,10 +67,22 @@ static struct key *integrity_keyring_from_id(const unsigned int id)
 	return keyring[id];
 }
 
+static struct key_tag *domain_tag_from_id(const unsigned int id)
+{
+	if (id >= INTEGRITY_KEYRING_MAX)
+		return ERR_PTR(-EINVAL);
+
+	if (id == INTEGRITY_KEYRING_IMA)
+		return current->nsproxy->ima_ns->key_domain;
+
+	return NULL;
+}
+
 int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
 			    const char *digest, int digestlen)
 {
 	struct key *keyring;
+	struct key_tag *domain_tag;
 
 	if (siglen < 2)
 		return -EINVAL;
@@ -68,14 +91,18 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
 	if (IS_ERR(keyring))
 		return PTR_ERR(keyring);
 
+	domain_tag = domain_tag_from_id(id);
+	if (IS_ERR(domain_tag))
+		return PTR_ERR(domain_tag);
+
 	switch (sig[1]) {
 	case 1:
 		/* v1 API expect signature without xattr type */
-		return digsig_verify(keyring, sig + 1, siglen - 1, digest,
-				     digestlen);
+		return digsig_verify(keyring, domain_tag,
+				     sig + 1, siglen - 1, digest, digestlen);
 	case 2:
-		return asymmetric_verify(keyring, sig, siglen, digest,
-					 digestlen);
+		return asymmetric_verify(keyring, domain_tag, sig, siglen,
+					 digest, digestlen);
 	}
 
 	return -EOPNOTSUPP;
@@ -101,7 +128,8 @@ static int __init __integrity_init_keyring(const unsigned int id,
 
 	keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0),
 				    KGIDT_INIT(0), cred, perm,
-				    KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL);
+				    keyring_alloc_flags[id],
+				    restriction, NULL);
 	if (IS_ERR(keyring[id])) {
 		err = PTR_ERR(keyring[id]);
 		pr_info("Can't allocate %s keyring (%d)\n",
@@ -153,7 +181,7 @@ int __init integrity_add_key(const unsigned int id, const void *data,
 
 	key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric",
 				   NULL, data, size, perm,
-				   KEY_ALLOC_NOT_IN_QUOTA);
+				   keyring_alloc_flags[id]);
 	if (IS_ERR(key)) {
 		rc = PTR_ERR(key);
 		pr_err("Problem loading X.509 certificate %d\n", rc);
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
index cfa4127d0518..4994a9773247 100644
--- a/security/integrity/digsig_asymmetric.c
+++ b/security/integrity/digsig_asymmetric.c
@@ -19,7 +19,9 @@
 /*
  * Request an asymmetric key.
  */
-static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
+static struct key *request_asymmetric_key(struct key *keyring,
+					  struct key_tag *domain_tag,
+					  uint32_t keyid)
 {
 	struct key *key;
 	char name[12];
@@ -44,14 +46,16 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
 		/* search in specific keyring */
 		key_ref_t kref;
 
-		kref = keyring_search(make_key_ref(keyring, 1),
-				      &key_type_asymmetric, name, true);
+		kref = keyring_search_tag(make_key_ref(keyring, 1),
+					  &key_type_asymmetric, name,
+					  domain_tag, true);
 		if (IS_ERR(kref))
 			key = ERR_CAST(kref);
 		else
 			key = key_ref_to_ptr(kref);
 	} else {
-		key = request_key(&key_type_asymmetric, name, NULL);
+		key = request_key_tag(&key_type_asymmetric,
+				      name, domain_tag, NULL);
 	}
 
 	if (IS_ERR(key)) {
@@ -73,8 +77,9 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
 	return key;
 }
 
-int asymmetric_verify(struct key *keyring, const char *sig,
-		      int siglen, const char *data, int datalen)
+int asymmetric_verify(struct key *keyring, struct key_tag *domain_tag,
+		      const char *sig, int siglen,
+		      const char *data, int datalen)
 {
 	struct public_key_signature pks;
 	struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
@@ -92,7 +97,8 @@ int asymmetric_verify(struct key *keyring, const char *sig,
 	if (hdr->hash_algo >= HASH_ALGO__LAST)
 		return -ENOPKG;
 
-	key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid));
+	key = request_asymmetric_key(keyring, domain_tag,
+				     be32_to_cpu(hdr->keyid));
 	if (IS_ERR(key))
 		return PTR_ERR(key);
 
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index c2981a98547e..207a1aef28e4 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -212,11 +212,14 @@ static inline int __init integrity_load_cert(const unsigned int id,
 #endif /* CONFIG_INTEGRITY_SIGNATURE */
 
 #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
-int asymmetric_verify(struct key *keyring, const char *sig,
-		      int siglen, const char *data, int datalen);
+int asymmetric_verify(struct key *keyring, struct key_tag *domain_tag,
+		      const char *sig, int siglen,
+		      const char *data, int datalen);
 #else
-static inline int asymmetric_verify(struct key *keyring, const char *sig,
-				    int siglen, const char *data, int datalen)
+static inline int asymmetric_verify(struct key *keyring,
+				    struct key_tag *domain_tag,
+				    const char *sig, int siglen,
+				    const char *data, int datalen)
 {
 	return -EOPNOTSUPP;
 }
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 30/30] ima: Set ML template per ima namespace
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Set ML template based on the ima_template string. It can be defined by
the user through kcmd_for_children ima securityfs entry. Acceptable
values are the same as for the ima_template kernel boot parameter.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h                   |  1 +
 security/integrity/ima/ima.h          |  1 +
 security/integrity/ima/ima_init.c     |  1 +
 security/integrity/ima/ima_ns.c       | 30 ++++++++++++++++++++++++++-
 security/integrity/ima/ima_template.c | 29 +++++++++++++++-----------
 5 files changed, 49 insertions(+), 13 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 7db4995c66cf..f65d6424e584 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -211,6 +211,7 @@ struct ima_namespace {
 #ifdef CONFIG_KEYS
 	struct key_tag *key_domain;
 #endif
+	struct ima_template_desc *ima_template;
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index b55d25c2bf63..89cb050d5668 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -177,6 +177,7 @@ int template_desc_init_fields(const char *template_fmt,
 struct ima_template_desc *ima_template_desc_current(void);
 struct ima_template_desc *lookup_template_desc(const char *name);
 bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
+int ima_template_setup(char *str, struct ima_namespace *ima_ns);
 int ima_restore_measurement_entry(struct ima_template_entry *entry);
 int ima_restore_measurement_list(loff_t bufsize, void *buf);
 int ima_measurements_show(struct seq_file *m, void *v);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 1668edf3ed32..af77d2c85964 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -49,6 +49,7 @@ struct ima_namespace init_ima_ns = {
 #ifdef CONFIG_KEYS
 	.key_domain = &init_ima_key_domain,
 #endif
+	.ima_template = NULL,
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 9b9c34e71cc6..05c5f0df8f8f 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -48,6 +48,28 @@ static void dec_ima_namespaces(struct ucounts *ucounts)
 	return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
 }
 
+static int ima_set_ns_template(struct ima_namespace *ima_ns)
+{
+	int result;
+
+	if (!ima_ns->ima_template)
+		ima_ns->ima_template =
+			lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
+
+	result = template_desc_init_fields(ima_ns->ima_template->fmt,
+					   &(ima_ns->ima_template->fields),
+					   &(ima_ns->ima_template->num_fields));
+	if (result < 0) {
+		pr_err("template %s init failed, result: %d\n",
+		       (strlen(ima_ns->ima_template->name) ?
+		       ima_ns->ima_template->name :
+		       ima_ns->ima_template->fmt), result);
+		ima_ns->ima_template = NULL;
+	}
+
+	return result;
+}
+
 static int ima_ns_add_boot_aggregate(struct ima_namespace *ima_ns)
 {
 	static const char op[] = "ns_add_boot_aggregate";
@@ -269,6 +291,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	ns->policy_path_for_children = NULL;
 	ns->x509_path_for_children = NULL;
 	ns->policy_setup_for_children = NULL;
+	ns->ima_template = NULL;
 
 	INIT_LIST_HEAD(&ns->policy_data->ima_default_rules);
 	INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules);
@@ -444,6 +467,10 @@ static int imans_activate(struct ima_namespace *ima_ns)
 	if (ima_ns->frozen)
 		goto out;
 
+	res = ima_set_ns_template(ima_ns);
+	if (res < 0)
+		goto out;
+
 	res = ima_set_ns_policy(ima_ns);
 	if (res < 0)
 		goto out;
@@ -580,10 +607,11 @@ struct ima_kernel_param {
 	int (*set)(char *val, struct ima_namespace *ima_ns);
 };
 
-/* TODO: add ima_template, ima_template_fmt, ima_hash, ... */
+/* TODO: add ima_template_fmt, ima_hash, ... */
 static const struct ima_kernel_param ima_kernel_params[] = {
 	{"ima_appraise", ima_default_appraise_setup},
 	{"ima_policy", ima_policy_setup},
+	{"ima_template", ima_template_setup},
 };
 static const size_t ima_kernel_params_size = ARRAY_SIZE(ima_kernel_params);
 
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 2020bd5176a4..91339a7e1134 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -57,8 +57,6 @@ static const struct ima_template_field supported_fields[] = {
  */
 #define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig|ns")
 
-static struct ima_template_desc *ima_template;
-
 /**
  * ima_template_has_modsig - Check whether template has modsig-related fields.
  * @ima_template: IMA template to check.
@@ -78,12 +76,12 @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template)
 	return false;
 }
 
-static int __init ima_template_setup(char *str)
+int ima_template_setup(char *str, struct ima_namespace *ima_ns)
 {
 	struct ima_template_desc *template_desc;
 	int template_len = strlen(str);
 
-	if (ima_template)
+	if (ima_ns->ima_template)
 		return 1;
 
 	ima_init_template_list();
@@ -109,16 +107,21 @@ static int __init ima_template_setup(char *str)
 		return 1;
 	}
 
-	ima_template = template_desc;
+	ima_ns->ima_template = template_desc;
 	return 1;
 }
-__setup("ima_template=", ima_template_setup);
+
+static int __init template_setup(char *str)
+{
+	return ima_template_setup(str, &init_ima_ns);
+}
+__setup("ima_template=", template_setup);
 
 static int __init ima_template_fmt_setup(char *str)
 {
 	int num_templates = ARRAY_SIZE(builtin_templates);
 
-	if (ima_template)
+	if (init_ima_ns.ima_template)
 		return 1;
 
 	if (template_desc_init_fields(str, NULL, NULL) < 0) {
@@ -128,7 +131,7 @@ static int __init ima_template_fmt_setup(char *str)
 	}
 
 	builtin_templates[num_templates - 1].fmt = str;
-	ima_template = builtin_templates + num_templates - 1;
+	init_ima_ns.ima_template = builtin_templates + num_templates - 1;
 
 	return 1;
 }
@@ -247,12 +250,14 @@ void ima_init_template_list(void)
 
 struct ima_template_desc *ima_template_desc_current(void)
 {
-	if (!ima_template) {
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	if (!ima_ns->ima_template) {
 		ima_init_template_list();
-		ima_template =
-		    lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
+		ima_ns->ima_template =
+			lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE);
 	}
-	return ima_template;
+	return ima_ns->ima_template;
 }
 
 int __init ima_init_template(void)
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 26/30] ima: Add key domain to the ima namespace
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add key domain to the ima namespace. This will allow to bind the
appraisal keys with the namespace and store all appraisal keys in the
ima system keyring.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h               |  3 +++
 security/integrity/ima/ima_init.c |  8 ++++++++
 security/integrity/ima/ima_ns.c   | 14 ++++++++++++++
 security/keys/key.c               | 10 +++++++---
 4 files changed, 32 insertions(+), 3 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 158028834747..7db4995c66cf 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -208,6 +208,9 @@ struct ima_namespace {
 	char *policy_path_for_children;
 	char *x509_path_for_children;
 	struct ima_policy_setup_data *policy_setup_for_children;
+#ifdef CONFIG_KEYS
+	struct key_tag *key_domain;
+#endif
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index d14c6689f422..1668edf3ed32 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -18,6 +18,7 @@
 #include <linux/kref.h>
 #include <linux/proc_ns.h>
 #include <linux/user_namespace.h>
+#include <linux/key.h>
 
 #include "ima.h"
 
@@ -25,6 +26,10 @@
 const char boot_aggregate_name[] = "boot_aggregate";
 struct tpm_chip *ima_tpm_chip;
 
+#ifdef CONFIG_KEYS
+static struct key_tag init_ima_key_domain = { .usage = REFCOUNT_INIT(1) };
+#endif
+
 struct ima_namespace init_ima_ns = {
 	.kref = KREF_INIT(2),
 	.user_ns = &init_user_ns,
@@ -41,6 +46,9 @@ struct ima_namespace init_ima_ns = {
 	.measurements = &ima_measurements,
 	.ml_len = ATOMIC_LONG_INIT(0),
 	.violations = ATOMIC_LONG_INIT(0),
+#ifdef CONFIG_KEYS
+	.key_domain = &init_ima_key_domain,
+#endif
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index ec3abc803c82..872dc6a96a96 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -28,6 +28,7 @@
 #include <linux/mutex.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
+#include <linux/key.h>
 
 #include "ima.h"
 
@@ -65,8 +66,16 @@ static struct ima_namespace *ima_ns_alloc(void)
 	if (!ima_ns->iint_tree)
 		goto policy_free;
 
+#ifdef CONFIG_KEYS
+	ima_ns->key_domain = kzalloc(sizeof(struct key_tag), GFP_KERNEL);
+	if (!ima_ns->key_domain)
+		goto iint_free;
+#endif
+
 	return ima_ns;
 
+iint_free:
+	kfree(ima_ns->iint_tree);
 policy_free:
 	kfree(ima_ns->policy_data);
 ns_free:
@@ -171,6 +180,9 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	rwlock_init(&ns->iint_tree->lock);
 	ns->iint_tree->root = RB_ROOT;
 
+#ifdef CONFIG_KEYS
+	refcount_set(&ns->key_domain->usage, 1);
+#endif
 	ns->policy_path_for_children = NULL;
 	ns->x509_path_for_children = NULL;
 	ns->policy_setup_for_children = NULL;
@@ -184,6 +196,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 fail_free:
 	kfree(ns->iint_tree);
 	kfree(ns->policy_data);
+	kfree(ns->key_domain);
 	kfree(ns);
 fail_dec:
 	dec_ima_namespaces(ucounts);
@@ -239,6 +252,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	bool is_init_ns = (ns == &init_ima_ns);
 
 	dec_ima_namespaces(ns->ucounts);
+	key_remove_domain(ns->key_domain);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	integrity_iint_tree_free(ns->iint_tree);
diff --git a/security/keys/key.c b/security/keys/key.c
index 1b0183d33bbc..fca0d12f5c71 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -285,10 +285,14 @@ struct key *key_alloc(struct key_type *type, const char *desc,
 
 		/* set domain tag if it's not predefined for the key type */
 		if ((!type->flags) && (flags & KEY_ALLOC_DOMAIN_IMA))
-			/* Set it to something meaningful after adding a key
-			 * domain to the ima namespace.
+			/* Use ima_ns_for_children, not ima_ns. ima_ns_for
+			 * children is equal to ima_ns, unless ima namespace was
+			 * unshared and the new namespace is being configured.
+			 * In that case, new keys should be associated with the
+			 * new ima namespace.
 			 */
-			key->index_key.domain_tag = NULL;
+			key->index_key.domain_tag =
+				current->nsproxy->ima_ns_for_children->key_domain;
 	}
 
 	key->index_key.desc_len = desclen;
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 29/30] ima: Add dummy boot aggregate to per ima namespace measurement list
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add dummy boot aggregate entry to the ima measurement list, for every
new ima namespace, when the first process is born into that namespace.

There is at most one TPM chip in the system and one measurement list
associated to one of its PCRs. IMA namespace IDs can be re-used after
namespace is destroyed. The per namespace boot aggregate entry marks
the moment of the ima namespace creation. It is useful when host's
root parses the global measurement list to find entries for destroyed
containers. If the ima namespace ID is reused, the user will know, that
the given entry belongs to a different container.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima_ns.c | 61 +++++++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 11e1d896f603..9b9c34e71cc6 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -48,6 +48,65 @@ static void dec_ima_namespaces(struct ucounts *ucounts)
 	return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
 }
 
+static int ima_ns_add_boot_aggregate(struct ima_namespace *ima_ns)
+{
+	static const char op[] = "ns_add_boot_aggregate";
+	static const char ns_aggregate_name_prefix[] = "ns_aggregate_";
+	const char *audit_cause = "ENOMEM";
+	struct ima_template_entry *entry;
+	struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
+	struct ima_event_data event_data = { .iint = iint };
+	int result = -ENOMEM;
+	int violation = 0;
+	struct {
+		struct ima_digest_data hdr;
+		char digest[TPM_DIGEST_SIZE];
+	} hash;
+	unsigned int ns_id = get_ns_id(ima_ns);
+	char *ns_aggregate_name;
+
+	ns_aggregate_name = kmalloc(sizeof(ns_aggregate_name_prefix) +
+				    sizeof(unsigned int),
+				    GFP_KERNEL);
+	if (!ns_aggregate_name)
+		goto err_out;
+
+	sprintf(ns_aggregate_name, "%s%u", ns_aggregate_name_prefix, ns_id);
+
+	event_data.filename = ns_aggregate_name;
+	event_data.ns_id = ns_id;
+
+	memset(iint, 0, sizeof(*iint));
+	memset(&hash, 0, sizeof(hash));
+	iint->ima_hash = &hash.hdr;
+	iint->ima_hash->algo = HASH_ALGO_SHA1;
+	iint->ima_hash->length = SHA1_DIGEST_SIZE;
+
+	result = ima_alloc_init_template(&event_data, &entry, NULL);
+	if (result < 0) {
+		audit_cause = "alloc_entry";
+		goto err_out;
+	}
+
+	result = ima_store_template(entry, violation, NULL,
+				    ns_aggregate_name,
+				    CONFIG_IMA_MEASURE_PCR_IDX,
+				    ima_ns);
+	if (result < 0) {
+		ima_free_template_entry(entry);
+		audit_cause = "store_entry";
+	}
+
+err_out:
+	if (result < 0)
+		integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL,
+				    ns_aggregate_name, op, audit_cause,
+				    result, 0);
+	kfree(ns_aggregate_name);
+
+	return result;
+}
+
 #ifdef CONFIG_IMA_LOAD_X509
 static int ima_ns_load_x509(struct ima_namespace *ima_ns)
 {
@@ -404,6 +463,8 @@ static int imans_activate(struct ima_namespace *ima_ns)
 	list_add_tail(&ima_ns->list, &ima_ns_list);
 	up_write(&ima_ns_list_lock);
 
+	ima_ns_add_boot_aggregate(ima_ns);
+
 	destroy_child_config(ima_ns);
 out:
 	mutex_unlock(&frozen_lock);
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 16/30] ima: Extend permissions to the ima securityfs entries
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add "others" permissions to the namespaced ima securityfs entries. It
is necessary so that the root in the user namespace that is the parent
of the given ima namespace has access to the ima related data.

Loosened DAC restrictrions are compensated by an extra check for
SYS_ADMIN capabilities in the ima code. The access is given
only to the namespaced data, e.g. root user in the new ima namespace
will see measurement list entries collected for that namespace and not
for the other existing namespaces. The only exception is made for the
admin in the initial user namespace, who has access to all the data.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima.h    |  4 ++--
 security/integrity/ima/ima_fs.c | 31 +++++++++++++++++++++++++++----
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e08f88aab0b5..7318fff3ccaa 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -523,9 +523,9 @@ static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
 #endif /* CONFIG_IMA_LSM_RULES */
 
 #ifdef	CONFIG_IMA_READ_POLICY
-#define	POLICY_FILE_FLAGS	(S_IWUSR | S_IRUSR)
+#define	POLICY_FILE_FLAGS	(S_IWUSR | S_IRUSR | S_IROTH | S_IWOTH)
 #else
-#define	POLICY_FILE_FLAGS	S_IWUSR
+#define	POLICY_FILE_FLAGS	(S_IWUSR | I_WOTH)
 #endif /* CONFIG_IMA_READ_POLICY */
 
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e2893f0b0f31..6d370874d80f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -59,6 +59,16 @@ static const struct file_operations ima_htable_violations_ops = {
 	.llseek = generic_file_llseek,
 };
 
+static int ima_open_measurements_count(struct inode *inode, struct file *file)
+{
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return 0;
+}
+
 static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
@@ -70,6 +80,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 }
 
 static const struct file_operations ima_measurements_count_ops = {
+	.open = ima_open_measurements_count,
 	.read = ima_show_measurements_count,
 	.llseek = generic_file_llseek,
 };
@@ -242,6 +253,11 @@ static const struct seq_operations ima_measurments_seqops = {
 
 static int ima_measurements_open(struct inode *inode, struct file *file)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
 	return seq_open(file, &ima_measurments_seqops);
 }
 
@@ -308,6 +324,11 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
 
 static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
 	return seq_open(file, &ima_ascii_measurements_seqops);
 }
 
@@ -429,13 +450,15 @@ static const struct seq_operations ima_policy_seqops = {
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
+
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
 		return -EACCES;
 #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!capable(CAP_SYS_ADMIN))
+		if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);
 #endif
@@ -509,21 +532,21 @@ int __init ima_fs_init(void)
 
 	binary_runtime_measurements =
 	    securityfs_create_file("binary_runtime_measurements",
-				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL,
 				   &ima_measurements_ops);
 	if (IS_ERR(binary_runtime_measurements))
 		goto out;
 
 	ascii_runtime_measurements =
 	    securityfs_create_file("ascii_runtime_measurements",
-				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL,
 				   &ima_ascii_measurements_ops);
 	if (IS_ERR(ascii_runtime_measurements))
 		goto out;
 
 	runtime_measurements_count =
 	    securityfs_create_file("runtime_measurements_count",
-				   S_IRUSR | S_IRGRP, ima_dir, NULL,
+				   S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL,
 				   &ima_measurements_count_ops);
 	if (IS_ERR(runtime_measurements_count))
 		goto out;
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 18/30] ima: Change the owning user namespace of the ima namespace if necessary
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

It's possible that the user first unshares the ima namespace and then
creates a new user namespace using clone3(). In that case the owning
user namespace is the newly created one, because it is associated with
the first process in the new ima namespace.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h             |  6 +++--
 kernel/nsproxy.c                |  2 +-
 security/integrity/ima/ima_ns.c | 42 +++++++++++++++++++++++++++------
 3 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 7eb7a008c5fe..4b5b832417b0 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -216,7 +216,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)
 {
@@ -239,7 +240,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 791efffd7a03..4b1ecf405f11 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 3012287b22d2..b8bfab39b55b 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -92,6 +92,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
  *
@@ -337,23 +355,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;
 }
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 19/30] ima: Configure the new ima namespace from securityfs
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add ima securityfs entries to configure per ima namespace:
- path to the policy file
- path to the x509 certificate
- ima kernel boot parameters

Policy file and x509 certificate will be parsed and loaded when the
first process is born into the new ima namespace, paths are not
validated when written. Kernel boot parameters are pre-parsed and the
same as above, the respective settings are applied when the first
process is born into the new namespace.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h                   |   3 +
 security/integrity/ima/ima.h          |  21 ++--
 security/integrity/ima/ima_appraise.c |  11 +-
 security/integrity/ima/ima_fs.c       | 172 +++++++++++++++++++++----
 security/integrity/ima/ima_init.c     |   3 +
 security/integrity/ima/ima_ns.c       | 173 +++++++++++++++++++++++---
 security/integrity/ima/ima_policy.c   |  40 +++---
 7 files changed, 358 insertions(+), 65 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 4b5b832417b0..158028834747 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -205,6 +205,9 @@ struct ima_namespace {
 	struct list_head *measurements;
 	atomic_long_t ml_len; /* number of stored measurements in the list */
 	atomic_long_t violations;
+	char *policy_path_for_children;
+	char *x509_path_for_children;
+	struct ima_policy_setup_data *policy_setup_for_children;
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index ef95522cc710..48d09efaffbe 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -62,6 +62,7 @@ struct ima_policy_setup_data {
 	int ima_appraise;
 	bool ima_use_secure_boot;
 	bool ima_use_appraise_tcb;
+	bool fail_unverifiable_sigs;
 };
 
 /* IMA event related data */
@@ -318,15 +319,10 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
 void ima_policy_stop(struct seq_file *m, void *v);
 int ima_policy_show(struct seq_file *m, void *v);
 
-int ima_policy_setup(char *str,
-		     struct ima_policy_setup_data *policy_setup_data,
-		     bool *fail_unverifiable_sigs);
-int ima_default_measure_policy_setup(const char *str,
-				     struct ima_policy_setup_data *setup_data);
-int ima_default_appraise_policy_setup(const char *str,
-				      struct ima_policy_setup_data *setup_data);
-int ima_default_appraise_setup(const char *str,
-			       struct ima_policy_setup_data *setup_data);
+int ima_policy_setup(char *str, struct ima_namespace *ima_ns);
+int ima_default_measure_policy_setup(char *str, struct ima_namespace *ima_ns);
+int ima_default_appraise_policy_setup(char *str, struct ima_namespace *ima_ns);
+int ima_default_appraise_setup(char *str, struct ima_namespace *ima_ns);
 
 /* Appraise integrity measurements */
 #define IMA_APPRAISE_ENFORCE	0x01
@@ -443,6 +439,13 @@ static inline struct ima_namespace *get_current_ns(void)
 
 void ima_delete_ns_rules(struct ima_policy_data *policy_data,
 			 bool is_root_ns);
+
+ssize_t ima_ns_write_policy_for_children(struct ima_namespace *ima_ns,
+					 char *policy_path);
+ssize_t ima_ns_write_x509_for_children(struct ima_namespace *ima_ns,
+				       char *x509_path);
+ssize_t ima_ns_write_kcmd_for_children(struct ima_namespace *ima_ns,
+				       char *kcmd);
 #else
 static inline int __init ima_init_namespace(void)
 {
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index a5e775182fb0..72fb4caeda96 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -16,9 +16,14 @@
 
 #include "ima.h"
 
-int ima_default_appraise_setup(const char *str,
-			       struct ima_policy_setup_data *setup_data)
+int ima_default_appraise_setup(char *str,
+			       struct ima_namespace *ima_ns)
 {
+	struct ima_policy_setup_data *setup_data;
+
+	setup_data = (ima_ns == &init_ima_ns) ?
+		&init_policy_setup_data : ima_ns->policy_setup_for_children;
+
 #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
 	if (arch_ima_get_secureboot()) {
 		pr_info("Secure boot enabled: ignoring ima_appraise=%s boot parameter option",
@@ -38,7 +43,7 @@ int ima_default_appraise_setup(const char *str,
 
 static int __init default_appraise_setup(char *str)
 {
-	return ima_default_appraise_setup(str, &init_policy_setup_data);
+	return ima_default_appraise_setup(str, &init_ima_ns);
 }
 
 __setup("ima_appraise=", default_appraise_setup);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index b71c2552ac15..6c033857f521 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -20,6 +20,8 @@
 #include <linux/rcupdate.h>
 #include <linux/parser.h>
 #include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
 
 #include "ima.h"
 
@@ -37,6 +39,16 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
 
 static int valid_policy = 1;
 
+static int ima_open_simple(struct inode *inode, struct file *file)
+{
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
+	return 0;
+}
+
 static ssize_t ima_show_htable_value(char __user *buf, size_t count,
 				     loff_t *ppos, atomic_long_t *val)
 {
@@ -56,32 +68,12 @@ static ssize_t ima_show_htable_violations(struct file *filp,
 	return ima_show_htable_value(buf, count, ppos, &ima_ns->violations);
 }
 
-static int ima_open_htable_violations(struct inode *inode, struct file *file)
-{
-	struct ima_namespace *ima_ns = get_current_ns();
-
-	if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
-		return -EPERM;
-
-	return 0;
-}
-
 static const struct file_operations ima_htable_violations_ops = {
-	.open = ima_open_htable_violations,
+	.open = ima_open_simple,
 	.read = ima_show_htable_violations,
 	.llseek = generic_file_llseek,
 };
 
-static int ima_open_measurements_count(struct inode *inode, struct file *file)
-{
-	struct ima_namespace *ima_ns = get_current_ns();
-
-	if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN))
-		return -EPERM;
-
-	return 0;
-}
-
 static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
@@ -92,7 +84,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 }
 
 static const struct file_operations ima_measurements_count_ops = {
-	.open = ima_open_measurements_count,
+	.open = ima_open_simple,
 	.read = ima_show_measurements_count,
 	.llseek = generic_file_llseek,
 };
@@ -441,6 +433,11 @@ static struct dentry *ascii_runtime_measurements;
 static struct dentry *runtime_measurements_count;
 static struct dentry *violations;
 static struct dentry *ima_policy;
+#ifdef CONFIG_IMA_NS
+static struct dentry *policy_for_children;
+static struct dentry *x509_for_children;
+static struct dentry *kcmd_for_children;
+#endif /* CONFIG_IMA_NS */
 
 enum ima_fs_flags {
 	IMA_FS_BUSY,
@@ -531,6 +528,109 @@ static const struct file_operations ima_measure_policy_ops = {
 	.llseek = generic_file_llseek,
 };
 
+#ifdef CONFIG_IMA_NS
+static int ima_open_for_children(struct inode *inode, struct file *file)
+{
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	/* Allow to set children configuration only after unshare() */
+	if (ima_ns == current->nsproxy->ima_ns_for_children)
+		return -EPERM;
+
+	return ima_open_simple(inode, file);
+}
+
+static ssize_t ima_write_policy_for_children(struct file *file,
+					     const char __user *buf,
+					     size_t count, loff_t *ppos)
+{
+	ssize_t res;
+	char *policy_path;
+	struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children;
+
+	/* Only allow < page size writes at the beginning of the file */
+	if ((*ppos != 0) || (count >= PAGE_SIZE))
+		return -EINVAL;
+
+	policy_path = memdup_user_nul(buf, count);
+	if (IS_ERR(policy_path))
+		return PTR_ERR(policy_path);
+
+	res = ima_ns_write_policy_for_children(ima_ns, policy_path);
+	if (res) {
+		kfree(policy_path);
+		count = res;
+	}
+
+	return count;
+}
+
+static const struct file_operations ima_policy_for_children_ops = {
+	.open = ima_open_for_children,
+	.write = ima_write_policy_for_children,
+};
+
+static ssize_t ima_write_x509_for_children(struct file *file,
+					   const char __user *buf,
+					   size_t count, loff_t *ppos)
+{
+	ssize_t res;
+	char *x509_path;
+	struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children;
+
+	/* Only allow < page size writes at the beginning of the file */
+	if ((*ppos != 0) || (count >= PAGE_SIZE))
+		return -EINVAL;
+
+	x509_path = memdup_user_nul(buf, count);
+	if (IS_ERR(x509_path))
+		return PTR_ERR(x509_path);
+
+	res = ima_ns_write_x509_for_children(ima_ns, x509_path);
+	if (res) {
+		kfree(x509_path);
+		count = res;
+	}
+
+	return count;
+}
+
+static const struct file_operations ima_x509_for_children_ops = {
+	.open = ima_open_for_children,
+	.write = ima_write_x509_for_children,
+};
+
+static ssize_t ima_write_kcmd_for_children(struct file *file,
+					   const char __user *buf,
+					   size_t count, loff_t *ppos)
+{
+	ssize_t res;
+	char *kcmd;
+	struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children;
+
+	/* Only allow < page size writes at the beginning of the file */
+	if ((*ppos != 0) || (count >= PAGE_SIZE))
+		return -EINVAL;
+
+	kcmd = memdup_user_nul(buf, count);
+	if (IS_ERR(kcmd))
+		return PTR_ERR(kcmd);
+
+	res = ima_ns_write_kcmd_for_children(ima_ns, kcmd);
+	if (res)
+		count = res;
+
+	kfree(kcmd);
+
+	return count;
+}
+
+static const struct file_operations ima_kcmd_for_children_ops = {
+	.open = ima_open_for_children,
+	.write = ima_write_kcmd_for_children,
+};
+#endif /* CONFIG_IMA_NS */
+
 int __init ima_fs_init(void)
 {
 	ima_dir = securityfs_create_dir("ima", integrity_dir);
@@ -575,6 +675,29 @@ int __init ima_fs_init(void)
 	if (IS_ERR(ima_policy))
 		goto out;
 
+#ifdef CONFIG_IMA_NS
+	policy_for_children = securityfs_create_file("policy_for_children",
+						0202,
+						ima_dir, NULL,
+						&ima_policy_for_children_ops);
+	if (IS_ERR(policy_for_children))
+		goto out;
+
+	x509_for_children = securityfs_create_file("x509_for_children",
+						  0202,
+						  ima_dir, NULL,
+						  &ima_x509_for_children_ops);
+	if (IS_ERR(x509_for_children))
+		goto out;
+
+	kcmd_for_children = securityfs_create_file("kcmd_for_children",
+						   0202,
+						   ima_dir, NULL,
+						   &ima_kcmd_for_children_ops);
+	if (IS_ERR(kcmd_for_children))
+		goto out;
+#endif /* CONFIG_IMA_NS */
+
 	return 0;
 out:
 	securityfs_remove(violations);
@@ -584,5 +707,10 @@ int __init ima_fs_init(void)
 	securityfs_remove(ima_symlink);
 	securityfs_remove(ima_dir);
 	securityfs_remove(ima_policy);
+#ifdef CONFIG_IMA_NS
+	securityfs_remove(policy_for_children);
+	securityfs_remove(x509_for_children);
+	securityfs_remove(kcmd_for_children);
+#endif /* CONFIG_IMA_NS */
 	return -1;
 }
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index f7b25b3f95de..d14c6689f422 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -31,6 +31,9 @@ struct ima_namespace init_ima_ns = {
 	.ns.inum = PROC_IMA_INIT_INO,
 #ifdef CONFIG_IMA_NS
 	.ns.ops = &imans_operations,
+	.policy_path_for_children = NULL,
+	.x509_path_for_children = NULL,
+	.policy_setup_for_children = NULL,
 #endif
 	.frozen = true,
 	.policy_data = &init_policy_data,
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index b8bfab39b55b..dd0328ee715f 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -26,6 +26,8 @@
 #include <linux/rwsem.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
 
 #include "ima.h"
 
@@ -73,23 +75,28 @@ static struct ima_namespace *ima_ns_alloc(void)
 	return NULL;
 }
 
-static void ima_set_ns_policy(struct ima_namespace *ima_ns,
-			      char *policy_setup_str)
+static int ima_set_ns_policy(struct ima_namespace *ima_ns)
 {
-	struct ima_policy_setup_data setup_data;
+	int result = 0;
+	struct ima_policy_setup_data setup_data = {0};
 
+	/* If namespace activation failed after policy initialization and
+	 * another process tries to join/activate the same namespace again,
+	 * old rules have to be removed.
+	 */
+	ima_delete_ns_rules(ima_ns->policy_data, false);
+
+	if (!ima_ns->policy_setup_for_children) {
 #ifdef CONFIG_IMA_APPRAISE
-	setup_data.ima_appraise = IMA_APPRAISE_ENFORCE;
+		setup_data.ima_appraise = IMA_APPRAISE_ENFORCE;
 #endif
-	/* Configuring IMA namespace will be implemented in the following
-	 * patches. When it is done, parse configuration string and store result
-	 * in setup_data. Temporarily use init_policy_setup_data.
-	 */
-	setup_data = init_policy_setup_data;
-	ima_ns->policy_data->ima_fail_unverifiable_sigs =
-		init_ima_ns.policy_data->ima_fail_unverifiable_sigs;
+		ima_init_ns_policy(ima_ns, &setup_data);
+		return result;
+	}
 
-	ima_init_ns_policy(ima_ns, &setup_data);
+	ima_init_ns_policy(ima_ns, ima_ns->policy_setup_for_children);
+
+	return result;
 }
 
 static int ima_swap_user_ns(struct ima_namespace *ima_ns,
@@ -150,6 +157,10 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	rwlock_init(&ns->iint_tree->lock);
 	ns->iint_tree->root = RB_ROOT;
 
+	ns->policy_path_for_children = NULL;
+	ns->x509_path_for_children = NULL;
+	ns->policy_setup_for_children = NULL;
+
 	INIT_LIST_HEAD(&ns->policy_data->ima_default_rules);
 	INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules);
 	INIT_LIST_HEAD(&ns->policy_data->ima_temp_rules);
@@ -199,6 +210,16 @@ int __init ima_init_namespace(void)
 	return 0;
 }
 
+static void destroy_child_config(struct ima_namespace *ima_ns)
+{
+	kfree(ima_ns->policy_path_for_children);
+	ima_ns->policy_path_for_children = NULL;
+	kfree(ima_ns->x509_path_for_children);
+	ima_ns->x509_path_for_children = NULL;
+	kfree(ima_ns->policy_setup_for_children);
+	ima_ns->policy_setup_for_children = NULL;
+}
+
 static void destroy_ima_ns(struct ima_namespace *ns)
 {
 	bool is_init_ns = (ns == &init_ima_ns);
@@ -210,6 +231,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	kfree(ns->iint_tree);
 	ima_delete_ns_rules(ns->policy_data, is_init_ns);
 	kfree(ns->policy_data);
+	destroy_child_config(ns);
 	kfree(ns);
 }
 
@@ -299,17 +321,21 @@ static void imans_put(struct ns_common *ns)
 
 static int imans_activate(struct ima_namespace *ima_ns)
 {
+	int res = 0;
+
 	if (ima_ns == &init_ima_ns)
-		return 0;
+		return res;
 
 	if (ima_ns->frozen)
-		return 0;
+		return res;
 
 	mutex_lock(&frozen_lock);
 	if (ima_ns->frozen)
 		goto out;
 
-	ima_set_ns_policy(ima_ns, NULL);
+	res = ima_set_ns_policy(ima_ns);
+	if (res < 0)
+		goto out;
 
 	ima_ns->frozen = true;
 
@@ -321,10 +347,12 @@ static int imans_activate(struct ima_namespace *ima_ns)
 	down_write(&ima_ns_list_lock);
 	list_add_tail(&ima_ns->list, &ima_ns_list);
 	up_write(&ima_ns_list_lock);
+
+	destroy_child_config(ima_ns);
 out:
 	mutex_unlock(&frozen_lock);
 
-	return 0;
+	return res;
 }
 
 static int imans_install(struct nsset *nsset, struct ns_common *new)
@@ -409,3 +437,116 @@ const struct proc_ns_operations imans_for_children_operations = {
 	.owner = imans_owner,
 };
 
+struct ima_kernel_param {
+	const char *name;
+	int (*set)(char *val, struct ima_namespace *ima_ns);
+};
+
+/* TODO: add ima_template, ima_template_fmt, ima_hash, ... */
+static const struct ima_kernel_param ima_kernel_params[] = {
+	{"ima_appraise", ima_default_appraise_setup},
+	{"ima_policy", ima_policy_setup},
+};
+static const size_t ima_kernel_params_size = ARRAY_SIZE(ima_kernel_params);
+
+ssize_t ima_ns_write_policy_for_children(struct ima_namespace *ima_ns,
+					 char *policy_path)
+{
+	ssize_t retval = 0;
+
+	mutex_lock(&frozen_lock);
+	if (ima_ns->frozen) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	kfree(ima_ns->policy_path_for_children);
+	ima_ns->policy_path_for_children = policy_path;
+out:
+	mutex_unlock(&frozen_lock);
+
+	return retval;
+}
+
+ssize_t ima_ns_write_x509_for_children(struct ima_namespace *ima_ns,
+				       char *x509_path)
+{
+	ssize_t retval = 0;
+
+	mutex_lock(&frozen_lock);
+	if (ima_ns->frozen) {
+		retval = -EACCES;
+		goto out;
+	}
+
+	kfree(ima_ns->x509_path_for_children);
+	ima_ns->x509_path_for_children = x509_path;
+out:
+	mutex_unlock(&frozen_lock);
+
+	return retval;
+}
+
+ssize_t ima_ns_write_kcmd_for_children(struct ima_namespace *ima_ns,
+				       char *kcmd)
+{
+	u32 i;
+	char *param, *val;
+	ssize_t ret = 0;
+
+	mutex_lock(&frozen_lock);
+	if (ima_ns->frozen) {
+		ret = -EACCES;
+		goto err_unlock;
+	}
+
+	if (!ima_ns->policy_setup_for_children) {
+		ima_ns->policy_setup_for_children =
+			kmalloc(sizeof(struct ima_policy_setup_data),
+				GFP_KERNEL);
+		if (!ima_ns->policy_setup_for_children) {
+			ret = -ENOMEM;
+			goto err_unlock;
+		}
+	}
+
+	memset(ima_ns->policy_setup_for_children,
+	       0, sizeof(struct ima_policy_setup_data));
+
+#ifdef CONFIG_IMA_APPRAISE
+	ima_ns->policy_setup_for_children->ima_appraise = IMA_APPRAISE_ENFORCE;
+#endif
+
+	kcmd = skip_spaces(kcmd);
+	while (*kcmd) {
+		kcmd = next_arg(kcmd, &param, &val);
+		if (!val) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+
+		for (i = 0; i < ima_kernel_params_size; i++) {
+			if (strcmp(param, ima_kernel_params[i].name) == 0)
+				break;
+		}
+
+		if (i == ima_kernel_params_size) {
+			ret = -EINVAL;
+			goto err_free;
+		}
+
+		ima_kernel_params[i].set(val, ima_ns);
+	}
+	mutex_unlock(&frozen_lock);
+
+	return ret;
+
+err_free:
+	kfree(ima_ns->policy_setup_for_children);
+	ima_ns->policy_setup_for_children = NULL;
+err_unlock:
+	mutex_unlock(&frozen_lock);
+
+	return ret;
+}
+
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 1f60ce9b2ffa..3d712c062ed0 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -217,9 +217,13 @@ struct ima_policy_data init_policy_data = {
 	.ima_rules = &init_policy_data.ima_default_rules,
 };
 
-int ima_default_measure_policy_setup(const char *str,
-				     struct ima_policy_setup_data *setup_data)
+int ima_default_measure_policy_setup(char *str, struct ima_namespace *ima_ns)
 {
+	struct ima_policy_setup_data *setup_data;
+
+	setup_data = (ima_ns == &init_ima_ns) ?
+		&init_policy_setup_data : ima_ns->policy_setup_for_children;
+
 	if (setup_data->ima_policy)
 		return 1;
 
@@ -229,7 +233,7 @@ int ima_default_measure_policy_setup(const char *str,
 
 static int __init default_measure_policy_setup(char *str)
 {
-	return ima_default_measure_policy_setup(str, &init_policy_setup_data);
+	return ima_default_measure_policy_setup(str, &init_ima_ns);
 }
 __setup("ima_tcb", default_measure_policy_setup);
 
@@ -238,15 +242,15 @@ static bool ima_fail_unverifiable_sigs __ro_after_init;
 /**
  * ima_policy_setup - parse policy configuration string "ima_policy="
  * @str: string to be parsed
- * @setup_data: pointer to a structure where parsed data is stored
- * @fail_unverifiable_sigs: boolean flag treated separately to preserve
- *                          __ro_after_init
+ * @ima_ns: pointer to the ima namespace which policy is being set
  */
-int ima_policy_setup(char *str,
-		     struct ima_policy_setup_data *setup_data,
-		     bool *fail_unverifiable_sigs)
+int ima_policy_setup(char *str, struct ima_namespace *ima_ns)
 {
 	char *p;
+	struct ima_policy_setup_data *setup_data;
+
+	setup_data = (ima_ns == &init_ima_ns) ?
+		&init_policy_setup_data : ima_ns->policy_setup_for_children;
 
 	while ((p = strsep(&str, " |\n")) != NULL) {
 		if (*p == ' ')
@@ -258,7 +262,7 @@ int ima_policy_setup(char *str,
 		else if (strcmp(p, "secure_boot") == 0)
 			setup_data->ima_use_secure_boot = true;
 		else if (strcmp(p, "fail_securely") == 0)
-			*fail_unverifiable_sigs = true;
+			setup_data->fail_unverifiable_sigs = true;
 	}
 
 	return 1;
@@ -266,21 +270,27 @@ int ima_policy_setup(char *str,
 
 static int __init policy_setup(char *str)
 {
-	return ima_policy_setup(str, &init_policy_setup_data,
-				&ima_fail_unverifiable_sigs);
+	ima_policy_setup(str, &init_ima_ns);
+	ima_fail_unverifiable_sigs =
+		init_policy_setup_data.fail_unverifiable_sigs;
+	return 1;
 }
 __setup("ima_policy=", policy_setup);
 
-int ima_default_appraise_policy_setup(const char *str,
-				      struct ima_policy_setup_data *setup_data)
+int ima_default_appraise_policy_setup(char *str, struct ima_namespace *ima_ns)
 {
+	struct ima_policy_setup_data *setup_data;
+
+	setup_data = (ima_ns == &init_ima_ns) ?
+		&init_policy_setup_data : ima_ns->policy_setup_for_children;
+
 	setup_data->ima_use_appraise_tcb = true;
 	return 1;
 }
 
 static int __init default_appraise_policy_setup(char *str)
 {
-	return ima_default_appraise_policy_setup(str, &init_policy_setup_data);
+	return ima_default_appraise_policy_setup(str, &init_ima_ns);
 }
 __setup("ima_appraise_tcb", default_appraise_policy_setup);
 
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 12/30] ima: Check ima namespace ID during digest entry lookup
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add ima namespace ID to the template entry data. It is not yet
included in the hash calculation and should be compared separately.
If template entry with the matching digest, pcr ID and ima namespace ID
is found, check if it belongs to the active ima namespace. This is
necessary because ima IDs are unique only among the active namespaces.
It is possible that the already destroyed namespace had added entry
with the same ID. In that case, create the new entry nontheless.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima.h       |  6 ++-
 security/integrity/ima/ima_api.c   | 12 ++++--
 security/integrity/ima/ima_init.c  |  3 +-
 security/integrity/ima/ima_main.c  |  2 +-
 security/integrity/ima/ima_queue.c | 60 ++++++++++++++++++++++++++----
 5 files changed, 67 insertions(+), 16 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7b7252d35d5a..e08f88aab0b5 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -155,7 +155,8 @@ int ima_init(void);
 int ima_fs_init(void);
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode,
-			   const unsigned char *filename);
+			   const unsigned char *filename,
+			   struct ima_namespace *ima_ns);
 int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
 int ima_calc_buffer_hash(const void *buf, loff_t len,
 			 struct ima_digest_data *hash);
@@ -294,7 +295,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
 			    struct ima_template_desc *template_desc);
 int ima_store_template(struct ima_template_entry *entry, int violation,
 		       struct inode *inode,
-		       const unsigned char *filename, int pcr);
+		       const unsigned char *filename, int pcr,
+		       struct ima_namespace *ima_ns);
 void ima_free_template_entry(struct ima_template_entry *entry);
 const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
 
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 1f4411fffa45..b01451b34a98 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -103,7 +103,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
  */
 int ima_store_template(struct ima_template_entry *entry,
 		       int violation, struct inode *inode,
-		       const unsigned char *filename, int pcr)
+		       const unsigned char *filename, int pcr,
+		       struct ima_namespace *ima_ns)
 {
 	static const char op[] = "add_template_measure";
 	static const char audit_cause[] = "hashing_error";
@@ -121,7 +122,8 @@ int ima_store_template(struct ima_template_entry *entry,
 		}
 	}
 	entry->pcr = pcr;
-	result = ima_add_template_entry(entry, violation, op, inode, filename);
+	result = ima_add_template_entry(entry, violation, op, inode, filename,
+					ima_ns);
 	return result;
 }
 
@@ -157,7 +159,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 		goto err_out;
 	}
 	result = ima_store_template(entry, violation, inode,
-				    filename, CONFIG_IMA_MEASURE_PCR_IDX);
+				    filename, CONFIG_IMA_MEASURE_PCR_IDX,
+				    ima_ns);
 	if (result < 0)
 		ima_free_template_entry(entry);
 err_out:
@@ -337,7 +340,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
 		return;
 	}
 
-	result = ima_store_template(entry, violation, inode, filename, pcr);
+	result = ima_store_template(entry, violation, inode, filename, pcr,
+				    ima_ns);
 	if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) {
 		iint->flags |= IMA_MEASURED;
 		iint->measured_pcrs |= (0x1 << pcr);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index aece357286b8..2100ee341dfc 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -105,7 +105,8 @@ static int __init ima_add_boot_aggregate(void)
 
 	result = ima_store_template(entry, violation, NULL,
 				    boot_aggregate_name,
-				    CONFIG_IMA_MEASURE_PCR_IDX);
+				    CONFIG_IMA_MEASURE_PCR_IDX,
+				    &init_ima_ns);
 	if (result < 0) {
 		ima_free_template_entry(entry);
 		audit_cause = "store_entry";
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index fa63780ae76e..b933c7e6c8e1 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -930,7 +930,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 		goto out;
 	}
 
-	ret = ima_store_template(entry, violation, NULL, buf, pcr);
+	ret = ima_store_template(entry, violation, NULL, buf, pcr, ima_ns);
 	if (ret < 0) {
 		audit_cause = "store_entry";
 		ima_free_template_entry(entry);
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index fb4ec270f620..bd890778c5be 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -46,7 +46,7 @@ static DEFINE_MUTEX(ima_extend_list_mutex);
 
 /* lookup up the digest value in the hash table, and return the entry */
 static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
-						       int pcr)
+						       int pcr, int ns_id)
 {
 	struct ima_queue_entry *qe, *ret = NULL;
 	unsigned int key;
@@ -57,7 +57,8 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
 	hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
 		rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
 			    digest_value, hash_digest_size[ima_hash_algo]);
-		if ((rc == 0) && (qe->entry->pcr == pcr)) {
+		if ((rc == 0) && (qe->entry->pcr == pcr) &&
+		    (qe->entry->ns_id == ns_id)) {
 			ret = qe;
 			break;
 		}
@@ -148,6 +149,38 @@ static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
 	return result;
 }
 
+#ifdef CONFIG_IMA_NS
+static bool ima_is_active_entry(const struct ima_queue_entry *qe,
+				const struct ima_namespace *ima_ns)
+{
+	bool found = false;
+	struct ima_queue_entry *ns_qe;
+
+	rcu_read_lock();
+	ns_qe = list_next_or_null_rcu(&ima_measurements, ima_ns->measurements,
+				      struct ima_queue_entry, later);
+	if (ns_qe) {
+		list_for_each_entry_from_rcu(ns_qe, &ima_measurements, later) {
+			found = memcmp(ns_qe->entry->digests[ima_hash_algo_idx].digest,
+				       qe->entry->digests[ima_hash_algo_idx].digest,
+				       hash_digest_size[ima_hash_algo]) == 0;
+			if (found)
+				break;
+
+		}
+	}
+	rcu_read_unlock();
+
+	return found;
+}
+#else
+static bool ima_is_active_entry(const struct ima_queue_entry *qe,
+				const struct ima_namespace *ima_ns)
+{
+	return true;
+}
+#endif /* CONFIG_IMA_NS */
+
 /*
  * Add template entry to the measurement list and hash table, and
  * extend the pcr.
@@ -158,7 +191,8 @@ static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
  */
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode,
-			   const unsigned char *filename)
+			   const unsigned char *filename,
+			   struct ima_namespace *ima_ns)
 {
 	u8 *digest = entry->digests[ima_hash_algo_idx].digest;
 	struct tpm_digest *digests_arg = entry->digests;
@@ -166,17 +200,27 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 	char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
 	int audit_info = 1;
 	int result = 0, tpmresult = 0;
+	struct ima_queue_entry *qe = NULL;
 
 	mutex_lock(&ima_extend_list_mutex);
 	if (!violation) {
-		if (ima_lookup_digest_entry(digest, entry->pcr)) {
-			audit_cause = "hash_exists";
-			result = -EEXIST;
-			goto out;
+		qe = ima_lookup_digest_entry(digest, entry->pcr, entry->ns_id);
+		if (qe) {
+			/* IMA ns IDs are guaranteed to be unique only among
+			 * active namespaces. It may happen that there is an
+			 * entry with matching ID for one of the destroyed
+			 * namespaces. Check if entry belongs to the active
+			 * namespace.
+			 */
+			if (ima_is_active_entry(qe, ima_ns)) {
+				audit_cause = "hash_exists";
+				result = -EEXIST;
+				goto out;
+			}
 		}
 	}
 
-	result = ima_add_digest_entry(entry, 1);
+	result = ima_add_digest_entry(entry, !qe);
 	if (result < 0) {
 		audit_cause = "ENOMEM";
 		audit_info = 0;
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 13/30] ima: Add a new ima template that includes namespace ID
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add a new ima-ns template:
"d-ng|n-ng|ns"

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima_template.c     |  5 ++++-
 security/integrity/ima/ima_template_lib.c | 13 +++++++++++++
 security/integrity/ima/ima_template_lib.h |  2 ++
 3 files changed, 19 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 945e70fafd2e..2020bd5176a4 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -22,6 +22,7 @@ static struct ima_template_desc builtin_templates[] = {
 	{.name = "ima-sig", .fmt = "d-ng|n-ng|sig"},
 	{.name = "ima-buf", .fmt = "d-ng|n-ng|buf"},
 	{.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"},
+	{.name = "ima-ns", .fmt = "d-ng|n-ng|ns"},
 	{.name = "", .fmt = ""},	/* placeholder for a custom format */
 };
 
@@ -45,6 +46,8 @@ static const struct ima_template_field supported_fields[] = {
 	 .field_show = ima_show_template_digest_ng},
 	{.field_id = "modsig", .field_init = ima_eventmodsig_init,
 	 .field_show = ima_show_template_sig},
+	{.field_id = "ns", .field_init = ima_eventns_init,
+	 .field_show = ima_show_template_buf},
 };
 
 /*
@@ -52,7 +55,7 @@ static const struct ima_template_field supported_fields[] = {
  * need to be accounted for since they shouldn't be defined in the same template
  * description as 'd-ng' and 'n-ng' respectively.
  */
-#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig")
+#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig|ns")
 
 static struct ima_template_desc *ima_template;
 
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c
index 635c6ac05050..cda5374dbbc4 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -484,3 +484,16 @@ int ima_eventmodsig_init(struct ima_event_data *event_data,
 	return ima_write_template_field_data(data, data_len, DATA_FMT_HEX,
 					     field_data);
 }
+
+/*
+ *  ima_eventns_init - include the ima namespace id as part of the
+ *  template data
+ */
+int ima_eventns_init(struct ima_event_data *event_data,
+		     struct ima_field_data *field_data)
+{
+	return ima_write_template_field_data(&(event_data->ns_id),
+					     sizeof(event_data->ns_id),
+					     DATA_FMT_HEX,
+					     field_data);
+}
diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h
index 9a88c79a7a61..7e67d1402192 100644
--- a/security/integrity/ima/ima_template_lib.h
+++ b/security/integrity/ima/ima_template_lib.h
@@ -46,4 +46,6 @@ int ima_eventbuf_init(struct ima_event_data *event_data,
 		      struct ima_field_data *field_data);
 int ima_eventmodsig_init(struct ima_event_data *event_data,
 			 struct ima_field_data *field_data);
+int ima_eventns_init(struct ima_event_data *event_data,
+		     struct ima_field_data *field_data);
 #endif /* __LINUX_IMA_TEMPLATE_LIB_H */
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 11/30] ima: Keep track of the measurment list per ima namespace
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski
In-Reply-To: <20200818154230.14016-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add a pointer to the tail of the measurement list to the ima namespace
when the namespace is created. This will allow to skip the irrelevant
measurement list entries while iterating the measurement list in that
ima namespace. Only entries with the matching ima namespace ID will be
displayed.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h               | 1 +
 security/integrity/ima/ima_init.c | 1 +
 security/integrity/ima/ima_ns.c   | 5 +++++
 3 files changed, 7 insertions(+)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 1d0439d86ade..df22143ffe30 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -202,6 +202,7 @@ struct ima_namespace {
 	bool frozen;
 	struct ima_policy_data *policy_data;
 	struct integrity_iint_tree *iint_tree;
+	struct list_head *measurements;
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index d63ecb02b032..aece357286b8 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -35,6 +35,7 @@ struct ima_namespace init_ima_ns = {
 	.frozen = true,
 	.policy_data = &init_policy_data,
 	.iint_tree = &init_iint_tree,
+	.measurements = &ima_measurements,
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 04aa50473971..f331187a4d3c 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -293,6 +293,11 @@ static int imans_activate(struct ima_namespace *ima_ns)
 
 	ima_ns->frozen = true;
 
+	/* Set current last element as list's head */
+	rcu_read_lock();
+	ima_ns->measurements = list_tail_rcu(&ima_measurements);
+	rcu_read_unlock();
+
 	down_write(&ima_ns_list_lock);
 	list_add_tail(&ima_ns->list, &ima_ns_list);
 	up_write(&ima_ns_list_lock);
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures
From: krzysztof.struczynski @ 2020-08-18 15:42 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: zohar, stefanb, sunyuqiong1988, mkayaalp, dmitry.kasatkin, serge,
	jmorris, christian, silviu.vlasceanu, roberto.sassu,
	Krzysztof Struczynski

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add ima namespace ID to the ima_event_data and ima_template_entry. This
is done so that the ima namespace ID can be tracked per entry and
included in the hash. The following patch will add a new template that
will utilize it.

IMA namespace ID is simply an inode number allocated to the namespace
when it's created and therefore it can be re-used once the namespace is
destroyed.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima.h          | 19 ++++++++++++++++++-
 security/integrity/ima/ima_api.c      |  9 ++++++++-
 security/integrity/ima/ima_fs.c       | 17 +++++++++++------
 security/integrity/ima/ima_init.c     |  2 ++
 security/integrity/ima/ima_kexec.c    |  4 +++-
 security/integrity/ima/ima_main.c     |  5 +++--
 security/integrity/ima/ima_template.c | 10 +++++++---
 7 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 33b4a8295c41..7b7252d35d5a 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -75,6 +75,7 @@ struct ima_event_data {
 	const char *violation;
 	const void *buf;
 	int buf_len;
+	unsigned int ns_id;
 };
 
 /* IMA template field data definition */
@@ -103,6 +104,7 @@ struct ima_template_desc {
 
 struct ima_template_entry {
 	int pcr;
+	unsigned int ns_id;
 	struct tpm_digest *digests;
 	struct ima_template_desc *template_desc; /* template descriptor */
 	u32 template_data_len;
@@ -129,8 +131,17 @@ extern const int read_idmap[];
 
 #ifdef CONFIG_HAVE_IMA_KEXEC
 void ima_load_kexec_buffer(void);
+extern const u16 kexec_header_version;
+static inline u16 get_kexec_header_version(void)
+{
+	return kexec_header_version;
+}
 #else
 static inline void ima_load_kexec_buffer(void) {}
+static inline u16 get_kexec_header_version(void)
+{
+	return 1;
+}
 #endif /* CONFIG_HAVE_IMA_KEXEC */
 
 /*
@@ -153,7 +164,8 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
 int ima_calc_boot_aggregate(struct ima_digest_data *hash);
 void ima_add_violation(struct file *file, const unsigned char *filename,
 		       struct integrity_iint_cache *iint,
-		       const char *op, const char *cause);
+		       const char *op, const char *cause,
+		       struct ima_namespace *ima_ns);
 int ima_init_crypto(void);
 void ima_putc(struct seq_file *m, void *data, int datalen);
 void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
@@ -415,6 +427,11 @@ extern struct ima_policy_setup_data init_policy_setup_data;
 extern struct list_head ima_ns_list;
 extern struct rw_semaphore ima_ns_list_lock;
 
+static inline unsigned int get_ns_id(const struct ima_namespace *ima_ns)
+{
+	return ima_ns->ns.inum;
+}
+
 #ifdef CONFIG_IMA_NS
 int __init ima_init_namespace(void);
 
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 8d7b0d4635fc..1f4411fffa45 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -76,6 +76,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
 		(*entry)->template_data_len += sizeof(len);
 		(*entry)->template_data_len += len;
 	}
+
+	(*entry)->ns_id = event_data->ns_id;
 	return 0;
 out:
 	ima_free_template_entry(*entry);
@@ -132,7 +134,8 @@ int ima_store_template(struct ima_template_entry *entry,
  */
 void ima_add_violation(struct file *file, const unsigned char *filename,
 		       struct integrity_iint_cache *iint,
-		       const char *op, const char *cause)
+		       const char *op, const char *cause,
+		       struct ima_namespace *ima_ns)
 {
 	struct ima_template_entry *entry;
 	struct inode *inode = file_inode(file);
@@ -143,6 +146,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 	int violation = 1;
 	int result;
 
+	event_data.ns_id = get_ns_id(ima_ns);
+
 	/* can overflow, only indicator */
 	atomic_long_inc(&ima_htable.violations);
 
@@ -313,7 +318,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
 					     .xattr_len = xattr_len,
 					     .modsig = modsig };
 	int violation = 0;
+	struct ima_namespace *ima_ns = get_current_ns();
 
+	event_data.ns_id = get_ns_id(ima_ns);
 	/*
 	 * We still need to store the measurement in the case of MODSIG because
 	 * we only have its contents to put in the list at the time of
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3839b9eaecab..4758e14c4a7b 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -117,6 +117,7 @@ void ima_putc(struct seq_file *m, void *data, int datalen)
 
 /* print format:
  *       32bit-le=pcr#
+ *       32bit-le=namespace id
  *       char[20]=template digest
  *       32bit-le=template name size
  *       char[n]=template name
@@ -129,7 +130,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
 	struct ima_queue_entry *qe = v;
 	struct ima_template_entry *e;
 	char *template_name;
-	u32 pcr, namelen, template_data_len; /* temporary fields */
+	u32 pcr, ns_id, namelen, template_data_len; /* temporary fields */
 	bool is_ima_template = false;
 	int i;
 
@@ -149,18 +150,22 @@ int ima_measurements_show(struct seq_file *m, void *v)
 	pcr = !ima_canonical_fmt ? e->pcr : cpu_to_le32(e->pcr);
 	ima_putc(m, &pcr, sizeof(e->pcr));
 
-	/* 2nd: template digest */
+	/* 2nd: ima namespace id */
+	ns_id = !ima_canonical_fmt ? e->ns_id : cpu_to_le32(e->ns_id);
+	ima_putc(m, &ns_id, sizeof(e->ns_id));
+
+	/* 3rd: template digest */
 	ima_putc(m, e->digests[ima_sha1_idx].digest, TPM_DIGEST_SIZE);
 
-	/* 3rd: template name size */
+	/* 4th: template name size */
 	namelen = !ima_canonical_fmt ? strlen(template_name) :
 		cpu_to_le32(strlen(template_name));
 	ima_putc(m, &namelen, sizeof(namelen));
 
-	/* 4th:  template name */
+	/* 5th:  template name */
 	ima_putc(m, template_name, strlen(template_name));
 
-	/* 5th:  template length (except for 'ima' template) */
+	/* 6th:  template length (except for 'ima' template) */
 	if (strcmp(template_name, IMA_TEMPLATE_IMA_NAME) == 0)
 		is_ima_template = true;
 
@@ -170,7 +175,7 @@ int ima_measurements_show(struct seq_file *m, void *v)
 		ima_putc(m, &template_data_len, sizeof(e->template_data_len));
 	}
 
-	/* 6th:  template specific data */
+	/* 7th:  template specific data */
 	for (i = 0; i < e->template_desc->num_fields; i++) {
 		enum ima_show_type show = IMA_SHOW_BINARY;
 		const struct ima_template_field *field =
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index d042b08cc4d7..d63ecb02b032 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -68,6 +68,8 @@ static int __init ima_add_boot_aggregate(void)
 		char digest[TPM_MAX_DIGEST_SIZE];
 	} hash;
 
+	event_data.ns_id = get_ns_id(&init_ima_ns);
+
 	memset(iint, 0, sizeof(*iint));
 	memset(&hash, 0, sizeof(hash));
 	iint->ima_hash = &hash.hdr;
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 121de3e04af2..1fefa59cf9b1 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -13,6 +13,8 @@
 #include "ima.h"
 
 #ifdef CONFIG_IMA_KEXEC
+const u16 kexec_header_version = 2;
+
 static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
 				     unsigned long segment_size)
 {
@@ -33,7 +35,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
 	file.count = sizeof(khdr);	/* reserved space */
 
 	memset(&khdr, 0, sizeof(khdr));
-	khdr.version = 1;
+	khdr.version = kexec_header_version;
 	list_for_each_entry_rcu(qe, &ima_measurements, later) {
 		if (file.count < file.size) {
 			khdr.count++;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 80b1737a3369..fa63780ae76e 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -134,10 +134,10 @@ static void ima_rdwr_violation_check(struct file *file,
 
 	if (send_tomtou)
 		ima_add_violation(file, *pathname, iint,
-				  "invalid_pcr", "ToMToU");
+				  "invalid_pcr", "ToMToU", ima_ns);
 	if (send_writers)
 		ima_add_violation(file, *pathname, iint,
-				  "invalid_pcr", "open_writers");
+				  "invalid_pcr", "open_writers", ima_ns);
 }
 
 static void ima_check_active_ns(struct ima_namespace *current_ima_ns,
@@ -923,6 +923,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 		goto out;
 	}
 
+	event_data.ns_id = get_ns_id(ima_ns);
 	ret = ima_alloc_init_template(&event_data, &entry, template);
 	if (ret < 0) {
 		audit_cause = "alloc_entry";
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 5a2def40a733..945e70fafd2e 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -13,7 +13,7 @@
 #include "ima.h"
 #include "ima_template_lib.h"
 
-enum header_fields { HDR_PCR, HDR_DIGEST, HDR_TEMPLATE_NAME,
+enum header_fields { HDR_PCR, HDR_NS_ID, HDR_DIGEST, HDR_TEMPLATE_NAME,
 		     HDR_TEMPLATE_DATA, HDR__LAST };
 
 static struct ima_template_desc builtin_templates[] = {
@@ -362,6 +362,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
 	struct ima_kexec_hdr *khdr = buf;
 	struct ima_field_data hdr[HDR__LAST] = {
 		[HDR_PCR] = {.len = sizeof(u32)},
+		[HDR_NS_ID] = {.len = sizeof(u32)},
 		[HDR_DIGEST] = {.len = TPM_DIGEST_SIZE},
 	};
 
@@ -382,7 +383,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
 		khdr->buffer_size = le64_to_cpu(khdr->buffer_size);
 	}
 
-	if (khdr->version != 1) {
+	if (khdr->version != get_kexec_header_version()) {
 		pr_err("attempting to restore a incompatible measurement list");
 		return -EINVAL;
 	}
@@ -394,11 +395,12 @@ int ima_restore_measurement_list(loff_t size, void *buf)
 
 	bitmap_zero(hdr_mask, HDR__LAST);
 	bitmap_set(hdr_mask, HDR_PCR, 1);
+	bitmap_set(hdr_mask, HDR_NS_ID, 1);
 	bitmap_set(hdr_mask, HDR_DIGEST, 1);
 
 	/*
 	 * ima kexec buffer prefix: version, buffer size, count
-	 * v1 format: pcr, digest, template-name-len, template-name,
+	 * v2 format: pcr, ns_id, digest, template-name-len, template-name,
 	 *	      template-data-size, template-data
 	 */
 	bufendp = buf + khdr->buffer_size;
@@ -470,6 +472,8 @@ int ima_restore_measurement_list(loff_t size, void *buf)
 
 		entry->pcr = !ima_canonical_fmt ? *(hdr[HDR_PCR].data) :
 			     le32_to_cpu(*(hdr[HDR_PCR].data));
+		entry->ns_id = !ima_canonical_fmt ? *(hdr[HDR_NS_ID].data) :
+			     le32_to_cpu(*(hdr[HDR_NS_ID].data));
 		ret = ima_restore_measurement_entry(entry);
 		if (ret < 0)
 			break;
-- 
2.20.1


^ permalink raw reply related

* Re: [PATCH bpf-next v8 5/7] bpf: Implement bpf_local_storage for inodes
From: KP Singh @ 2020-08-18 15:33 UTC (permalink / raw)
  To: Martin KaFai Lau
  Cc: linux-kernel, bpf, linux-security-module, Alexei Starovoitov,
	Daniel Borkmann, Paul Turner, Jann Horn, Florent Revest
In-Reply-To: <20200818152316.pkyko6gcpzeqp5sn@kafai-mbp.dhcp.thefacebook.com>



On 8/18/20 5:23 PM, Martin KaFai Lau wrote:
> On Tue, Aug 18, 2020 at 05:10:34PM +0200, KP Singh wrote:
>>
>>
>> On 8/18/20 3:27 AM, Martin KaFai Lau wrote:>>> On Mon, Aug 03, 2020 at 06:46:53PM +0200, KP Singh wrote:

[...]

a get_file
>> rather fcheck followed by get_file_rcu:
>>
>> #define get_file_rcu_many(x, cnt)	\
>> 	atomic_long_add_unless(&(x)->f_count, (cnt), 0)
>> #define get_file_rcu(x) get_file_rcu_many((x), 1)
>> #define file_count(x)	atomic_long_read(&(x)->f_count)
>>
>> But there is an easier way than all of this and this is to use 
>> fget_raw which also calls get_file_rcu_many 
>> and ensures a non-zero count before getting a reference.
> ic. Make sense.
> 
> There are fdget() and fdput() also which are used in bpf/syscall.c.

Yeah we could use fdget_raw but we don't really need the struct fd but just the 
struct file.

he non-raw versions masks away FMODE_PATH (O_PATH) files, we should still be able to
access blobs on the O_PATH files, thus the _raw version here.

- KP

> 

^ permalink raw reply

* [RFC PATCH 05/30] ima: Add methods for parsing ima policy configuration string
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

IMA subsystem is configured at boot time using kernel command-line
parameters, e.g.: ima_policy=tcb|appraise_tcb|secure_boot. The same
configuration options should be available for the new ima namespace.
Add new functions to parse configuration string and store parsed data
in the new policy data structures. Don't implement it yet, just add the
dummy interface.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima.h          | 10 ++++++++
 security/integrity/ima/ima_appraise.c |  9 +++++++
 security/integrity/ima/ima_policy.c   | 37 +++++++++++++++++++++++++++
 3 files changed, 56 insertions(+)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0d88222e3500..4872f193f7a3 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -306,6 +306,16 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
 void ima_policy_stop(struct seq_file *m, void *v);
 int ima_policy_show(struct seq_file *m, void *v);
 
+int ima_policy_setup(char *str,
+		     struct ima_policy_setup_data *policy_setup_data,
+		     bool *fail_unverifiable_sigs);
+int ima_default_measure_policy_setup(const char *str,
+				     struct ima_policy_setup_data *setup_data);
+int ima_default_appraise_policy_setup(const char *str,
+				      struct ima_policy_setup_data *setup_data);
+int ima_default_appraise_setup(const char *str,
+			       struct ima_policy_setup_data *setup_data);
+
 /* Appraise integrity measurements */
 #define IMA_APPRAISE_ENFORCE	0x01
 #define IMA_APPRAISE_FIX	0x02
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 372d16382960..0632d3881611 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -16,6 +16,15 @@
 
 #include "ima.h"
 
+int ima_default_appraise_setup(const char *str,
+			       struct ima_policy_setup_data *setup_data)
+{
+	/* Currently unused. It will be implemented after namespacing ima
+	 * policy, when global variables are removed.
+	 */
+	return 1;
+}
+
 static int __init default_appraise_setup(char *str)
 {
 #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 6b56741ec1c9..403854b18ef2 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -223,6 +223,15 @@ struct ima_policy_data init_policy_data = {
 	.ima_temp_rules = LIST_HEAD_INIT(init_policy_data.ima_temp_rules),
 };
 
+int ima_default_measure_policy_setup(const char *str,
+				     struct ima_policy_setup_data *setup_data)
+{
+	/* Currently unused. It will be implemented after namespacing ima
+	 * policy, when global variables are removed.
+	 */
+	return 1;
+}
+
 static int __init default_measure_policy_setup(char *str)
 {
 	if (ima_policy)
@@ -236,6 +245,25 @@ __setup("ima_tcb", default_measure_policy_setup);
 static bool ima_use_appraise_tcb __initdata;
 static bool ima_use_secure_boot __initdata;
 static bool ima_fail_unverifiable_sigs __ro_after_init;
+
+/**
+ * ima_policy_setup - parse policy configuration string "ima_policy="
+ * @str: string to be parsed
+ * @setup_data: pointer to a structure where parsed data is stored
+ * @fail_unverifiable_sigs: boolean flag treated separately to preserve
+ * __ro_after_init
+ */
+int ima_policy_setup(char *str,
+		     struct ima_policy_setup_data *setup_data,
+		     bool *fail_unverifiable_sigs)
+{
+
+	/* Currently unused. It will be implemented after namespacing ima
+	 * policy, when global variables are removed.
+	 */
+	return 1;
+}
+
 static int __init policy_setup(char *str)
 {
 	char *p;
@@ -257,6 +285,15 @@ static int __init policy_setup(char *str)
 }
 __setup("ima_policy=", policy_setup);
 
+int ima_default_appraise_policy_setup(const char *str,
+				      struct ima_policy_setup_data *setup_data)
+{
+	/* Currently unused. It will be implemented after namespacing ima
+	 * policy, when global variables are removed.
+	 */
+	return 1;
+}
+
 static int __init default_appraise_policy_setup(char *str)
 {
 	ima_use_appraise_tcb = true;
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 04/30] ima: Add ima policy related data to the ima namespace
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Collate global variables describing the ima policy in one structure and
add it to the ima namespace. Collate setup data (parsed kernel boot
parameters) in a separate structure.

Per namespace policy is not yet properly set and it is not used. This
will be done in the following patches.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h                 |  2 ++
 security/integrity/ima/ima.h        | 24 +++++++++++++++++
 security/integrity/ima/ima_init.c   |  3 ++-
 security/integrity/ima/ima_ns.c     | 41 +++++++++++++++++++++++++++--
 security/integrity/ima/ima_policy.c | 26 ++++++++++++++++++
 5 files changed, 93 insertions(+), 3 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 3954cef57c00..d61c9c21ffb9 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -16,6 +16,7 @@ struct nsproxy;
 struct task_struct;
 struct list_head;
 struct llist_node;
+struct ima_policy_data;
 
 #ifdef CONFIG_IMA
 extern int ima_bprm_check(struct linux_binprm *bprm);
@@ -188,6 +189,7 @@ struct ima_namespace {
 	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;
+	struct ima_policy_data *policy_data;
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 092e87190c6d..0d88222e3500 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -60,6 +60,14 @@ extern int ima_appraise;
 extern struct tpm_chip *ima_tpm_chip;
 extern const char boot_aggregate_name[];
 
+/* IMA policy setup data */
+struct ima_policy_setup_data {
+	int ima_policy;
+	int ima_appraise;
+	bool ima_use_secure_boot;
+	bool ima_use_appraise_tcb;
+};
+
 /* IMA event related data */
 struct ima_event_data {
 	struct integrity_iint_cache *iint;
@@ -286,6 +294,8 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
 		     struct ima_template_desc **template_desc,
 		     const char *keyring);
 void ima_init_policy(void);
+void ima_init_ns_policy(struct ima_namespace *ima_ns,
+			const struct ima_policy_setup_data *policy_setup_data);
 void ima_update_policy(void);
 void ima_update_policy_flag(void);
 ssize_t ima_parse_add_rule(char *);
@@ -372,6 +382,20 @@ static inline int ima_read_xattr(struct dentry *dentry,
 
 #endif /* CONFIG_IMA_APPRAISE */
 
+struct ima_policy_data {
+	struct list_head ima_default_rules;
+	struct list_head ima_policy_rules;
+	struct list_head ima_temp_rules;
+	struct list_head *ima_rules;
+	bool ima_fail_unverifiable_sigs;
+	int ima_policy_flag; /* current content of the policy */
+	int ima_appraise;
+	int temp_ima_appraise;
+};
+
+extern struct ima_policy_data init_policy_data;
+extern struct ima_policy_setup_data init_policy_setup_data;
+
 extern struct list_head ima_ns_list;
 extern struct rw_semaphore ima_ns_list_lock;
 
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 0ba04a1a68cc..ea5ff42eb7fe 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -32,7 +32,8 @@ struct ima_namespace init_ima_ns = {
 #ifdef CONFIG_IMA_NS
 	.ns.ops = &imans_operations,
 #endif
-	.frozen = true
+	.frozen = true,
+	.policy_data = &init_policy_data,
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 3a98cd536d05..1aeb9cfeb3a2 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -51,9 +51,38 @@ static struct ima_namespace *ima_ns_alloc(void)
 
 	ima_ns = kzalloc(sizeof(*ima_ns), GFP_KERNEL);
 	if (!ima_ns)
-		return NULL;
+		goto out;
+
+	ima_ns->policy_data = kzalloc(sizeof(struct ima_policy_data),
+				      GFP_KERNEL);
+	if (!ima_ns->policy_data)
+		goto out_free;
 
 	return ima_ns;
+
+out_free:
+	kfree(ima_ns);
+out:
+	return NULL;
+}
+
+static void ima_set_ns_policy(struct ima_namespace *ima_ns,
+			      char *policy_setup_str)
+{
+	struct ima_policy_setup_data setup_data;
+
+#ifdef CONFIG_IMA_APPRAISE
+	setup_data.ima_appraise = IMA_APPRAISE_ENFORCE;
+#endif
+	/* Configuring IMA namespace will be implemented in the following
+	 * patches. When it is done, parse configuration string and store result
+	 * in setup_data. Temporarily use init_policy_setup_data.
+	 */
+	setup_data = init_policy_setup_data;
+	ima_ns->policy_data->ima_fail_unverifiable_sigs =
+		init_ima_ns.policy_data->ima_fail_unverifiable_sigs;
+
+	ima_init_ns_policy(ima_ns, &setup_data);
 }
 
 /**
@@ -64,7 +93,7 @@ static struct ima_namespace *ima_ns_alloc(void)
  * Return:	ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise
  */
 static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
-					   struct ima_namespace *old_ns)
+					  struct ima_namespace *old_ns)
 {
 	struct ima_namespace *ns;
 	struct ucounts *ucounts;
@@ -91,9 +120,14 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	ns->ucounts = ucounts;
 	ns->frozen = false;
 
+	INIT_LIST_HEAD(&ns->policy_data->ima_default_rules);
+	INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules);
+	INIT_LIST_HEAD(&ns->policy_data->ima_temp_rules);
+
 	return ns;
 
 fail_free:
+	kfree(ns->policy_data);
 	kfree(ns);
 fail_dec:
 	dec_ima_namespaces(ucounts);
@@ -139,6 +173,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	dec_ima_namespaces(ns->ucounts);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
+	kfree(ns->policy_data);
 	kfree(ns);
 }
 
@@ -238,6 +273,8 @@ static int imans_activate(struct ima_namespace *ima_ns)
 	if (ima_ns->frozen)
 		goto out;
 
+	ima_set_ns_policy(ima_ns, NULL);
+
 	ima_ns->frozen = true;
 
 	down_write(&ima_ns_list_lock);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 07f033634b27..6b56741ec1c9 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -212,6 +212,17 @@ static size_t ima_keyrings_len;
 
 static int ima_policy __initdata;
 
+struct ima_policy_setup_data init_policy_setup_data = {
+#ifdef CONFIG_IMA_APPRAISE
+	.ima_appraise = IMA_APPRAISE_ENFORCE,
+#endif
+};
+struct ima_policy_data init_policy_data = {
+	.ima_default_rules = LIST_HEAD_INIT(init_policy_data.ima_default_rules),
+	.ima_policy_rules = LIST_HEAD_INIT(init_policy_data.ima_policy_rules),
+	.ima_temp_rules = LIST_HEAD_INIT(init_policy_data.ima_temp_rules),
+};
+
 static int __init default_measure_policy_setup(char *str)
 {
 	if (ima_policy)
@@ -715,6 +726,21 @@ static int __init ima_init_arch_policy(void)
 	return i;
 }
 
+/**
+ * ima_init_ns_policy - initialize the default measure rules.
+ * @ima_ns: pointer to the namespace whose rules are being initialized
+ * @setup_data: pointer to the policy setup data
+ */
+void ima_init_ns_policy(struct ima_namespace *ima_ns,
+			const struct ima_policy_setup_data *setup_data)
+{
+	/* Set policy rules to the empty set of default rules. The rest will be
+	 * implemented after namespacing policy.
+	 */
+	ima_ns->policy_data->ima_rules =
+		&ima_ns->policy_data->ima_default_rules;
+}
+
 /**
  * ima_init_policy - initialize the default measure rules.
  *
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 02/30] ima: Add a list of the installed ima namespaces
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

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>
---
 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 4a9c29d4d056..5b6235b97603 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -10,10 +10,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);
@@ -176,6 +178,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 603da5b2db08..092e87190c6d 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -372,12 +372,23 @@ static inline int ima_read_xattr(struct dentry *dentry,
 
 #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 013bbec16849..0ba04a1a68cc 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);
 
@@ -154,6 +155,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 8a91711ca79b..d800e73c8b62 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -37,6 +37,9 @@ int ima_appraise;
 int ima_hash_algo = HASH_ALGO_SHA1;
 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)
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 09/30] ima: Enable per ima namespace policy settings
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Set ima policy per namespace and remove the global settings. Operations
on the objects may now have impact in more than one ima namespace and
therefore iterate all active ima namespaces when necessary.

Read-write violations can now happen across namespaces and should be
checked in all namespaces for each relevant ima hook.

Inform all concerned ima namespaces about the actions on the objects
when the object is freed. E.g. if an object had been appraised in the
ima_ns_1 and then modified in the ima_ns_2, appraised flag in the
ima_ns_1 is cleared and the object will be re-appraised in the ima_ns_1
namespace.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h                          |  11 +
 kernel/kexec_file.c                          |   7 +
 security/integrity/ima/ima.h                 |  10 +-
 security/integrity/ima/ima_api.c             |   2 +-
 security/integrity/ima/ima_appraise.c        |  99 ++++---
 security/integrity/ima/ima_asymmetric_keys.c |  13 +-
 security/integrity/ima/ima_fs.c              |   6 +-
 security/integrity/ima/ima_init.c            |   7 +-
 security/integrity/ima/ima_main.c            | 235 ++++++++++++----
 security/integrity/ima/ima_ns.c              |   3 +
 security/integrity/ima/ima_policy.c          | 278 +++++++++++--------
 security/integrity/ima/ima_queue_keys.c      |  10 +-
 security/security.c                          |   2 +-
 13 files changed, 462 insertions(+), 221 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 9069aafd905f..1d0439d86ade 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -33,6 +33,8 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
 extern void ima_post_path_mknod(struct dentry *dentry);
 extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
 extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
+extern void ima_inode_free(struct inode *inode);
+extern bool ima_is_root_namespace(void);
 
 #ifdef CONFIG_IMA_KEXEC
 extern void ima_add_kexec_buffer(struct kimage *image);
@@ -116,6 +118,15 @@ static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
 }
 
 static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
+
+static inline void ima_inode_free(struct inode *inode)
+{
+}
+
+static inline bool ima_is_root_namespace(void)
+{
+	return true;
+}
 #endif /* CONFIG_IMA */
 
 #ifndef CONFIG_IMA_KEXEC
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 78c0837bfd7b..e17542057dfb 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -356,6 +356,13 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
 	if (!capable(CAP_SYS_BOOT) || kexec_load_disabled)
 		return -EPERM;
 
+	/* Allow only from the initial IMA namespace, so that the user can't
+	 * spawn a new IMA namespace with the empty policy and circumvent the
+	 * appraisal protection.
+	 */
+	if (!ima_is_root_namespace())
+		return -EPERM;
+
 	/* Make sure we have a legal set of flags */
 	if (flags != (flags & KEXEC_FILE_FLAGS))
 		return -EINVAL;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7d522fdab0d8..33b4a8295c41 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -48,15 +48,11 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
 
 #define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0)
 
-/* current content of the policy */
-extern int ima_policy_flag;
-
 /* set during initialization */
 extern int ima_hash_algo;
 extern int ima_sha1_idx __ro_after_init;
 extern int ima_hash_algo_idx __ro_after_init;
 extern int ima_extra_slots __ro_after_init;
-extern int ima_appraise;
 extern struct tpm_chip *ima_tpm_chip;
 extern const char boot_aggregate_name[];
 
@@ -410,6 +406,9 @@ struct ima_policy_data {
 	int temp_ima_appraise;
 };
 
+extern struct list_head ima_ns_list;
+extern struct rw_semaphore ima_ns_list_lock;
+
 extern struct ima_policy_data init_policy_data;
 extern struct ima_policy_setup_data init_policy_setup_data;
 
@@ -423,6 +422,9 @@ static inline struct ima_namespace *get_current_ns(void)
 {
 	return current->nsproxy->ima_ns;
 }
+
+void ima_delete_ns_rules(struct ima_policy_data *policy_data,
+			 bool is_root_ns);
 #else
 static inline int __init ima_init_namespace(void)
 {
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 8b41183200e8..8d7b0d4635fc 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -192,7 +192,7 @@ int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
 {
 	int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
 
-	flags &= ima_policy_flag;
+	flags &= ima_ns->policy_data->ima_policy_flag;
 
 	return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
 				template_desc, keyring, ima_ns);
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 9388ff88ca4d..a5e775182fb0 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -18,14 +18,6 @@
 
 int ima_default_appraise_setup(const char *str,
 			       struct ima_policy_setup_data *setup_data)
-{
-	/* Currently unused. It will be implemented after namespacing ima
-	 * policy, when global variables are removed.
-	 */
-	return 1;
-}
-
-static int __init default_appraise_setup(char *str)
 {
 #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
 	if (arch_ima_get_secureboot()) {
@@ -35,15 +27,20 @@ static int __init default_appraise_setup(char *str)
 	}
 
 	if (strncmp(str, "off", 3) == 0)
-		ima_appraise = 0;
+		setup_data->ima_appraise = 0;
 	else if (strncmp(str, "log", 3) == 0)
-		ima_appraise = IMA_APPRAISE_LOG;
+		setup_data->ima_appraise = IMA_APPRAISE_LOG;
 	else if (strncmp(str, "fix", 3) == 0)
-		ima_appraise = IMA_APPRAISE_FIX;
+		setup_data->ima_appraise = IMA_APPRAISE_FIX;
 #endif
 	return 1;
 }
 
+static int __init default_appraise_setup(char *str)
+{
+	return ima_default_appraise_setup(str, &init_policy_setup_data);
+}
+
 __setup("ima_appraise=", default_appraise_setup);
 
 /*
@@ -54,7 +51,10 @@ __setup("ima_appraise=", default_appraise_setup);
  */
 bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
 {
-	return ima_appraise & IMA_APPRAISE_ENFORCE;
+	if (!ima_ns)
+		return false;
+
+	return ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE;
 }
 
 /*
@@ -62,18 +62,18 @@ bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
  *
  * Return 1 to appraise or hash
  */
-int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
-		      struct ima_namespace *ima_ns)
+int ima_must_appraise(struct inode *inode, int mask,
+		      enum ima_hooks func, struct ima_namespace *ima_ns)
 {
 	u32 secid;
 
-	if (!ima_appraise)
+	if (!ima_ns->policy_data->ima_appraise)
 		return 0;
 
 	security_task_getsecid(current, &secid);
 	return ima_match_policy(inode, current_cred(), secid, func, mask,
 				IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL,
-				NULL);
+				ima_ns);
 }
 
 static int ima_fix_xattr(struct dentry *dentry,
@@ -349,7 +349,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
 		if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
 			process_buffer_measurement(NULL, digest, digestsize,
 						   "blacklisted-hash", NONE,
-						   pcr, NULL, NULL);
+						   pcr, NULL, ima_ns);
 	}
 
 	return rc;
@@ -376,6 +376,7 @@ int ima_appraise_measurement(enum ima_hooks func,
 	enum integrity_status status = INTEGRITY_UNKNOWN;
 	int rc = xattr_len;
 	bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	/* If not appraising a modsig, we need an xattr. */
 	if (!(inode->i_opflags & IOP_XATTR) && !try_modsig)
@@ -448,7 +449,8 @@ int ima_appraise_measurement(enum ima_hooks func,
 				    op, cause, rc, 0);
 	} else if (status != INTEGRITY_PASS) {
 		/* Fix mode, but don't replace file signatures. */
-		if ((ima_appraise & IMA_APPRAISE_FIX) && !try_modsig &&
+		if ((ima_ns->policy_data->ima_appraise & IMA_APPRAISE_FIX) &&
+		    !try_modsig &&
 		    (!xattr_value ||
 		     xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
 			if (!ima_fix_xattr(dentry, iint))
@@ -510,20 +512,32 @@ void ima_inode_post_setattr(struct dentry *dentry)
 	struct inode *inode = d_backing_inode(dentry);
 	struct integrity_iint_cache *iint;
 	int action;
+	struct ima_namespace *ima_ns;
 
-	if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
-	    || !(inode->i_opflags & IOP_XATTR))
+	if (!S_ISREG(inode->i_mode) ||
+	    !(inode->i_opflags & IOP_XATTR))
 		return;
 
-	action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR, NULL);
-	if (!action)
-		__vfs_removexattr(dentry, XATTR_NAME_IMA);
-	iint = integrity_iint_find(inode);
-	if (iint) {
-		set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
+		if (!(ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE))
+			continue;
+
+		action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR,
+					   ima_ns);
 		if (!action)
-			clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+			__vfs_removexattr(dentry, XATTR_NAME_IMA);
+		iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
+		if (iint) {
+			set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
+			if (!action)
+				clear_bit(IMA_UPDATE_XATTR,
+					  &iint->atomic_flags);
+		}
 	}
+	up_read(&ima_ns_list_lock);
 }
 
 /*
@@ -545,19 +559,30 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
 static void ima_reset_appraise_flags(struct inode *inode, int digsig)
 {
 	struct integrity_iint_cache *iint;
+	struct ima_namespace *ima_ns;
 
-	if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
+	if (!S_ISREG(inode->i_mode))
 		return;
 
-	iint = integrity_iint_find(inode);
-	if (!iint)
-		return;
-	iint->measured_pcrs = 0;
-	set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
-	if (digsig)
-		set_bit(IMA_DIGSIG, &iint->atomic_flags);
-	else
-		clear_bit(IMA_DIGSIG, &iint->atomic_flags);
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
+		if (!(ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE))
+			continue;
+
+		iint = integrity_iint_find(inode);
+		if (!iint)
+			continue;
+
+		iint->measured_pcrs = 0;
+		set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
+		if (digsig)
+			set_bit(IMA_DIGSIG, &iint->atomic_flags);
+		else
+			clear_bit(IMA_DIGSIG, &iint->atomic_flags);
+	}
+	up_read(&ima_ns_list_lock);
 }
 
 int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 58aa56b0422d..b3330a0a1481 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -29,6 +29,16 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 				   unsigned long flags, bool create)
 {
 	bool queued = false;
+	/* Measure the keys according to the current ima namespace's policy
+	 * rules. If the new ima namespace with empty policy is created to hide
+	 * the log, parent can join it to inspect the log until the child
+	 * namespace exists. After its destruction, log can be accessed only
+	 * by the processes from the initial ima namespace that see all
+	 * measurement list entries. If this is a problem, maybe the solution
+	 * is to track in which namespaces the key was measured and re-measure
+	 * it when necessary.
+	 */
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	/* Only asymmetric keys are handled by this hook. */
 	if (key->type != &key_type_asymmetric)
@@ -60,6 +70,5 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 	 */
 	process_buffer_measurement(NULL, payload, payload_len,
 				   keyring->description, KEY_CHECK, 0,
-				   keyring->description,
-				   NULL);
+				   keyring->description, ima_ns);
 }
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 97aadee7e68e..3839b9eaecab 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -312,6 +312,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 {
 	char *data;
 	ssize_t result;
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	if (datalen >= PAGE_SIZE)
 		datalen = PAGE_SIZE - 1;
@@ -333,7 +334,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 
 	if (data[0] == '/') {
 		result = ima_read_policy(data);
-	} else if (ima_appraise & IMA_APPRAISE_POLICY) {
+	} else if (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_POLICY) {
 		pr_err("signed policy file (specified as an absolute pathname) required\n");
 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
 				    "policy_update", "signed policy required",
@@ -406,11 +407,12 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 static int ima_release_policy(struct inode *inode, struct file *file)
 {
 	const char *cause = valid_policy ? "completed" : "failed";
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
 
-	if (valid_policy && ima_check_policy(NULL) < 0) {
+	if (valid_policy && ima_check_policy(ima_ns) < 0) {
 		cause = "failed";
 		valid_policy = 0;
 	}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index be1afc42fdf5..d042b08cc4d7 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -118,11 +118,12 @@ static int __init ima_add_boot_aggregate(void)
 #ifdef CONFIG_IMA_LOAD_X509
 void __init ima_load_x509(void)
 {
-	int unset_flags = ima_policy_flag & IMA_APPRAISE;
+	int unset_flags =
+		init_ima_ns.policy_data->ima_policy_flag & IMA_APPRAISE;
 
-	ima_policy_flag &= ~unset_flags;
+	init_ima_ns.policy_data->ima_policy_flag &= ~unset_flags;
 	integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
-	ima_policy_flag |= unset_flags;
+	init_ima_ns.policy_data->ima_policy_flag |= unset_flags;
 }
 #endif
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 196fa2bd490d..80b1737a3369 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -28,12 +28,6 @@
 
 #include "ima.h"
 
-#ifdef CONFIG_IMA_APPRAISE
-int ima_appraise = IMA_APPRAISE_ENFORCE;
-#else
-int ima_appraise;
-#endif
-
 int ima_hash_algo = HASH_ALGO_SHA1;
 static int hash_setup_done;
 
@@ -119,7 +113,8 @@ static void ima_rdwr_violation_check(struct file *file,
 	if (mode & FMODE_WRITE) {
 		if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
 			if (!iint)
-				iint = integrity_iint_find(inode);
+				iint = integrity_iint_rb_find(ima_ns->iint_tree,
+							      inode);
 			/* IMA_MEASURE is set from reader side */
 			if (iint && test_bit(IMA_MUST_MEASURE,
 						&iint->atomic_flags))
@@ -145,11 +140,38 @@ static void ima_rdwr_violation_check(struct file *file,
 				  "invalid_pcr", "open_writers");
 }
 
+static void ima_check_active_ns(struct ima_namespace *current_ima_ns,
+				struct inode *inode)
+{
+	struct ima_namespace *ima_ns;
+	struct integrity_iint_cache *iint;
+
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
+		if ((ima_ns == current_ima_ns) ||
+		    !ima_ns->policy_data->ima_policy_flag)
+			continue;
+
+		iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
+		if (!iint)
+			continue;
+
+		mutex_lock(&iint->mutex);
+		iint->flags &= ~IMA_DONE_MASK;
+		iint->measured_pcrs = 0;
+		mutex_unlock(&iint->mutex);
+	}
+	up_read(&ima_ns_list_lock);
+}
+
 static void ima_check_last_writer(struct integrity_iint_cache *iint,
 				  struct inode *inode, struct file *file)
 {
 	fmode_t mode = file->f_mode;
 	bool update;
+	struct ima_namespace *ima_ns = (struct ima_namespace *)file->f_ima;
 
 	if (!(mode & FMODE_WRITE))
 		return;
@@ -163,6 +185,9 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
 		    (iint->flags & IMA_NEW_FILE)) {
 			iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
 			iint->measured_pcrs = 0;
+
+			ima_check_active_ns(ima_ns, inode);
+
 			if (update)
 				ima_update_xattr(iint, file);
 		}
@@ -202,10 +227,10 @@ void ima_file_free(struct file *file)
 	if (unlikely(!(file->f_mode & FMODE_OPENED)))
 		goto out;
 
-	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ima_ns->policy_data->ima_policy_flag || !S_ISREG(inode->i_mode))
 		goto out;
 
-	iint = integrity_iint_find(inode);
+	iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
 	if (!iint)
 		goto out;
 
@@ -214,10 +239,10 @@ void ima_file_free(struct file *file)
 	put_ima_ns(ima_ns);
 }
 
-static int process_measurement(struct file *file, const struct cred *cred,
-			       u32 secid, char *buf, loff_t size, int mask,
-			       enum ima_hooks func,
-			       struct ima_namespace *ima_ns)
+static int process_ns_measurement(struct file *file, const struct cred *cred,
+				  u32 secid, char *buf, loff_t size, int mask,
+				  enum ima_hooks func,
+				  struct ima_namespace *ima_ns)
 {
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
@@ -232,8 +257,9 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	int xattr_len = 0;
 	bool violation_check;
 	enum hash_algo hash_algo;
+	struct ima_namespace *current_ima_ns = get_current_ns();
 
-	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ima_ns->policy_data->ima_policy_flag)
 		return 0;
 
 	/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
@@ -243,7 +269,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	action = ima_get_action(inode, cred, secid, mask, func, &pcr,
 				&template_desc, NULL, ima_ns);
 	violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
-			   (ima_policy_flag & IMA_MEASURE));
+			   (ima_ns->policy_data->ima_policy_flag &
+			    IMA_MEASURE));
 	if (!action && !violation_check)
 		return 0;
 
@@ -256,7 +283,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	inode_lock(inode);
 
 	if (action) {
-		iint = integrity_inode_get(inode);
+		iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
 		if (!iint)
 			rc = -ENOMEM;
 	}
@@ -271,6 +298,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
 		goto out;
 	if (!action)
 		goto out;
+	if (ima_ns != current_ima_ns)
+		goto out;
 
 	mutex_lock(&iint->mutex);
 
@@ -389,7 +418,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	if (pathbuf)
 		__putname(pathbuf);
 	if (must_appraise) {
-		if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE))
+		if (rc &&
+		    (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE))
 			return -EACCES;
 		if (file->f_mode & FMODE_WRITE)
 			set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
@@ -397,6 +427,32 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	return 0;
 }
 
+static int process_measurement(struct file *file, const struct cred *cred,
+			       u32 secid, char *buf, loff_t size, int mask,
+			       enum ima_hooks func)
+{
+	int ret;
+	struct ima_namespace *ima_ns;
+	struct inode *inode = file_inode(file);
+
+	if (!S_ISREG(inode->i_mode))
+		return 0;
+
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
+
+		ret = process_ns_measurement(file, cred, secid, buf, size, mask,
+					     func, ima_ns);
+		if (ret != 0)
+			break;
+	}
+	up_read(&ima_ns_list_lock);
+
+	return ret;
+}
+
 /**
  * ima_file_mmap - based on policy, collect/store measurement.
  * @file: pointer to the file to be measured (May be NULL)
@@ -415,7 +471,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
 	if (file && (prot & PROT_EXEC)) {
 		security_task_getsecid(current, &secid);
 		return process_measurement(file, current_cred(), secid, NULL,
-					   0, MAY_EXEC, MMAP_CHECK, NULL);
+					   0, MAY_EXEC, MMAP_CHECK);
 	}
 
 	return 0;
@@ -435,6 +491,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
  */
 int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
 	struct ima_template_desc *template;
 	struct file *file = vma->vm_file;
 	char filename[NAME_MAX];
@@ -447,14 +504,15 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 	int pcr;
 
 	/* Is mprotect making an mmap'ed file executable? */
-	if (!(ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
-	    !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
+	if (!(ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE) ||
+	    !vma->vm_file || !(prot & PROT_EXEC) ||
+	    (vma->vm_flags & VM_EXEC))
 		return 0;
 
 	security_task_getsecid(current, &secid);
 	inode = file_inode(vma->vm_file);
 	action = ima_get_action(inode, current_cred(), secid, MAY_EXEC,
-				MMAP_CHECK, &pcr, &template, 0, NULL);
+				MMAP_CHECK, &pcr, &template, 0, ima_ns);
 
 	/* Is the mmap'ed file in policy? */
 	if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
@@ -493,13 +551,13 @@ int ima_bprm_check(struct linux_binprm *bprm)
 
 	security_task_getsecid(current, &secid);
 	ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
-				  MAY_EXEC, BPRM_CHECK, NULL);
+				  MAY_EXEC, BPRM_CHECK);
 	if (ret)
 		return ret;
 
 	security_cred_getsecid(bprm->cred, &secid);
 	return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
-				   MAY_EXEC, CREDS_CHECK, NULL);
+				   MAY_EXEC, CREDS_CHECK);
 }
 
 /**
@@ -519,13 +577,13 @@ int ima_file_check(struct file *file, int mask)
 	security_task_getsecid(current, &secid);
 	return process_measurement(file, current_cred(), secid, NULL, 0,
 				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
-					   MAY_APPEND), FILE_CHECK, NULL);
+					   MAY_APPEND), FILE_CHECK);
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
 /**
  * ima_file_hash - return the stored measurement if a file has been hashed and
- * is in the iint cache.
+ * is in the iint cache of the current IMA namespace.
  * @file: pointer to the file
  * @buf: buffer in which to store the hash
  * @buf_size: length of the buffer
@@ -543,6 +601,7 @@ EXPORT_SYMBOL_GPL(ima_file_check);
  */
 int ima_file_hash(struct file *file, char *buf, size_t buf_size)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
 	struct inode *inode;
 	struct integrity_iint_cache *iint;
 	int hash_algo;
@@ -550,11 +609,11 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
 	if (!file)
 		return -EINVAL;
 
-	if (!ima_policy_flag)
+	if (!ima_ns->policy_data->ima_policy_flag)
 		return -EOPNOTSUPP;
 
 	inode = file_inode(file);
-	iint = integrity_iint_find(inode);
+	iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
 	if (!iint)
 		return -EOPNOTSUPP;
 
@@ -582,21 +641,30 @@ EXPORT_SYMBOL_GPL(ima_file_hash);
  */
 void ima_post_create_tmpfile(struct inode *inode)
 {
+	struct ima_namespace *ima_ns;
 	struct integrity_iint_cache *iint;
 	int must_appraise;
 
-	must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
-	if (!must_appraise)
-		return;
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
 
-	/* Nothing to do if we can't allocate memory */
-	iint = integrity_inode_get(inode);
-	if (!iint)
-		return;
+		must_appraise = ima_must_appraise(inode, MAY_ACCESS,
+						  FILE_CHECK, ima_ns);
+		if (!must_appraise)
+			continue;
+
+		/* Nothing to do if we can't allocate memory */
+		iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
+		if (!iint)
+			continue;
 
-	/* needed for writing the security xattrs */
-	set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
-	iint->ima_file_status = INTEGRITY_PASS;
+		/* needed for writing the security xattrs */
+		set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+		iint->ima_file_status = INTEGRITY_PASS;
+	}
+	up_read(&ima_ns_list_lock);
 }
 
 /**
@@ -608,21 +676,30 @@ void ima_post_create_tmpfile(struct inode *inode)
  */
 void ima_post_path_mknod(struct dentry *dentry)
 {
+	struct ima_namespace *ima_ns;
 	struct integrity_iint_cache *iint;
 	struct inode *inode = dentry->d_inode;
 	int must_appraise;
 
-	must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
-	if (!must_appraise)
-		return;
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
 
-	/* Nothing to do if we can't allocate memory */
-	iint = integrity_inode_get(inode);
-	if (!iint)
-		return;
+		must_appraise = ima_must_appraise(inode, MAY_ACCESS,
+						  FILE_CHECK, ima_ns);
+		if (!must_appraise)
+			continue;
 
-	/* needed for re-opening empty files */
-	iint->flags |= IMA_NEW_FILE;
+		/* Nothing to do if we can't allocate memory */
+		iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
+		if (!iint)
+			continue;
+
+		/* needed for re-opening empty files */
+		iint->flags |= IMA_NEW_FILE;
+	}
+	up_read(&ima_ns_list_lock);
 }
 
 /**
@@ -676,10 +753,13 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 {
 	enum ima_hooks func;
 	u32 secid;
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	if (!file && read_id == READING_FIRMWARE) {
-		if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
-		    (ima_appraise & IMA_APPRAISE_ENFORCE)) {
+		if ((ima_ns->policy_data->ima_appraise &
+		     IMA_APPRAISE_FIRMWARE) &&
+		    (ima_ns->policy_data->ima_appraise &
+		     IMA_APPRAISE_ENFORCE)) {
 			pr_err("Prevent firmware loading_store.\n");
 			return -EACCES;	/* INTEGRITY_UNKNOWN */
 		}
@@ -691,7 +771,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 		return 0;
 
 	if (!file || !buf || size == 0) { /* should never happen */
-		if (ima_appraise & IMA_APPRAISE_ENFORCE)
+		if (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE)
 			return -EACCES;
 		return 0;
 	}
@@ -699,7 +779,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 	func = read_idmap[read_id] ?: FILE_CHECK;
 	security_task_getsecid(current, &secid);
 	return process_measurement(file, current_cred(), secid, buf, size,
-				   MAY_READ, func, NULL);
+				   MAY_READ, func);
 }
 
 /**
@@ -715,9 +795,16 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 int ima_load_data(enum kernel_load_data_id id)
 {
 	bool ima_enforce, sig_enforce;
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	if (ima_ns != &init_ima_ns) {
+		pr_err("Prevent data loading in IMA namespaces other than the root\n");
+		return -EACCES;
+	}
 
 	ima_enforce =
-		(ima_appraise & IMA_APPRAISE_ENFORCE) == IMA_APPRAISE_ENFORCE;
+		(ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE) ==
+		IMA_APPRAISE_ENFORCE;
 
 	switch (id) {
 	case LOADING_KEXEC_IMAGE:
@@ -727,13 +814,16 @@ int ima_load_data(enum kernel_load_data_id id)
 			return -EACCES;
 		}
 
-		if (ima_enforce && (ima_appraise & IMA_APPRAISE_KEXEC)) {
+		if (ima_enforce &&
+		    (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_KEXEC)) {
 			pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n");
 			return -EACCES;	/* INTEGRITY_UNKNOWN */
 		}
 		break;
 	case LOADING_FIRMWARE:
-		if (ima_enforce && (ima_appraise & IMA_APPRAISE_FIRMWARE)) {
+		if (ima_enforce &&
+		    (ima_ns->policy_data->ima_appraise &
+		     IMA_APPRAISE_FIRMWARE)) {
 			pr_err("Prevent firmware sysfs fallback loading.\n");
 			return -EACCES;	/* INTEGRITY_UNKNOWN */
 		}
@@ -741,8 +831,10 @@ int ima_load_data(enum kernel_load_data_id id)
 	case LOADING_MODULE:
 		sig_enforce = is_module_sig_enforced();
 
-		if (ima_enforce && (!sig_enforce
-				    && (ima_appraise & IMA_APPRAISE_MODULES))) {
+		if (ima_enforce &&
+		    (!sig_enforce &&
+		     (ima_ns->policy_data->ima_appraise &
+		      IMA_APPRAISE_MODULES))) {
 			pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n");
 			return -EACCES;	/* INTEGRITY_UNKNOWN */
 		}
@@ -761,6 +853,7 @@ int ima_load_data(enum kernel_load_data_id id)
  * @func: IMA hook
  * @pcr: pcr to extend the measurement
  * @keyring: keyring name to determine the action to be performed
+ * @ima_ns: pointer to the IMA namespace in consideration
  *
  * Based on policy, the buffer is measured into the ima log.
  */
@@ -786,7 +879,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 	int action = 0;
 	u32 secid;
 
-	if (!ima_policy_flag)
+	if (!ima_ns->policy_data->ima_policy_flag)
 		return;
 
 	/*
@@ -799,7 +892,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 	if (func) {
 		security_task_getsecid(current, &secid);
 		action = ima_get_action(inode, current_cred(), secid, 0, func,
-					&pcr, &template, keyring, NULL);
+					&pcr, &template, keyring, ima_ns);
 		if (!(action & IMA_MEASURE))
 			return;
 	}
@@ -862,6 +955,11 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 {
 	struct fd f;
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	/* Currently allowed only from the root IMA namespace */
+	if (WARN_ON(ima_ns != &init_ima_ns))
+		return;
 
 	if (!buf || !size)
 		return;
@@ -872,10 +970,31 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 
 	process_buffer_measurement(file_inode(f.file), buf, size,
 				   "kexec-cmdline", KEXEC_CMDLINE, 0, NULL,
-				   NULL);
+				   ima_ns);
 	fdput(f);
 }
 
+void ima_inode_free(struct inode *inode)
+{
+	struct ima_namespace *ima_ns;
+
+	if (!IS_IMA(inode))
+		return;
+
+	down_read(&ima_ns_list_lock);
+	list_for_each_entry(ima_ns, &ima_ns_list, list) {
+		if (atomic_read(&ima_ns->inactive))
+			continue;
+		integrity_inode_rb_free(ima_ns->iint_tree, inode);
+	}
+	up_read(&ima_ns_list_lock);
+}
+
+bool ima_is_root_namespace(void)
+{
+	return get_current_ns() == &init_ima_ns;
+}
+
 static int __init init_ima(void)
 {
 	int error;
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 226a53279f71..04aa50473971 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -181,11 +181,14 @@ int __init ima_init_namespace(void)
 
 static void destroy_ima_ns(struct ima_namespace *ns)
 {
+	bool is_init_ns = (ns == &init_ima_ns);
+
 	dec_ima_namespaces(ns->ucounts);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
 	integrity_iint_tree_free(ns->iint_tree);
 	kfree(ns->iint_tree);
+	ima_delete_ns_rules(ns->policy_data, is_init_ns);
 	kfree(ns->policy_data);
 	kfree(ns);
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 12f9dcf73c83..1f60ce9b2ffa 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -46,8 +46,6 @@
 #define INVALID_PCR(a) (((a) < 0) || \
 	(a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8))
 
-int ima_policy_flag;
-static int temp_ima_appraise;
 static int build_ima_appraise __ro_after_init;
 
 #define MAX_LSM_RULES 6
@@ -198,20 +196,15 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
 	 .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
 };
 
+/* Number of architecture specific rules found */
+static int arch_entries_size __ro_after_init;
 /* An array of architecture specific rules */
 static struct ima_rule_entry *arch_policy_entry __ro_after_init;
 
-static LIST_HEAD(ima_default_rules);
-static LIST_HEAD(ima_policy_rules);
-static LIST_HEAD(ima_temp_rules);
-static struct list_head *ima_rules = &ima_default_rules;
-
 /* Pre-allocated buffer used for matching keyrings. */
 static char *ima_keyrings;
 static size_t ima_keyrings_len;
 
-static int ima_policy __initdata;
-
 struct ima_policy_setup_data init_policy_setup_data = {
 #ifdef CONFIG_IMA_APPRAISE
 	.ima_appraise = IMA_APPRAISE_ENFORCE,
@@ -221,29 +214,25 @@ struct ima_policy_data init_policy_data = {
 	.ima_default_rules = LIST_HEAD_INIT(init_policy_data.ima_default_rules),
 	.ima_policy_rules = LIST_HEAD_INIT(init_policy_data.ima_policy_rules),
 	.ima_temp_rules = LIST_HEAD_INIT(init_policy_data.ima_temp_rules),
+	.ima_rules = &init_policy_data.ima_default_rules,
 };
 
 int ima_default_measure_policy_setup(const char *str,
 				     struct ima_policy_setup_data *setup_data)
 {
-	/* Currently unused. It will be implemented after namespacing ima
-	 * policy, when global variables are removed.
-	 */
+	if (setup_data->ima_policy)
+		return 1;
+
+	setup_data->ima_policy = ORIGINAL_TCB;
 	return 1;
 }
 
 static int __init default_measure_policy_setup(char *str)
 {
-	if (ima_policy)
-		return 1;
-
-	ima_policy = ORIGINAL_TCB;
-	return 1;
+	return ima_default_measure_policy_setup(str, &init_policy_setup_data);
 }
 __setup("ima_tcb", default_measure_policy_setup);
 
-static bool ima_use_appraise_tcb __initdata;
-static bool ima_use_secure_boot __initdata;
 static bool ima_fail_unverifiable_sigs __ro_after_init;
 
 /**
@@ -251,53 +240,47 @@ static bool ima_fail_unverifiable_sigs __ro_after_init;
  * @str: string to be parsed
  * @setup_data: pointer to a structure where parsed data is stored
  * @fail_unverifiable_sigs: boolean flag treated separately to preserve
- * __ro_after_init
+ *                          __ro_after_init
  */
 int ima_policy_setup(char *str,
 		     struct ima_policy_setup_data *setup_data,
 		     bool *fail_unverifiable_sigs)
-{
-
-	/* Currently unused. It will be implemented after namespacing ima
-	 * policy, when global variables are removed.
-	 */
-	return 1;
-}
-
-static int __init policy_setup(char *str)
 {
 	char *p;
 
 	while ((p = strsep(&str, " |\n")) != NULL) {
 		if (*p == ' ')
 			continue;
-		if ((strcmp(p, "tcb") == 0) && !ima_policy)
-			ima_policy = DEFAULT_TCB;
+		if ((strcmp(p, "tcb") == 0) && !setup_data->ima_policy)
+			setup_data->ima_policy = DEFAULT_TCB;
 		else if (strcmp(p, "appraise_tcb") == 0)
-			ima_use_appraise_tcb = true;
+			setup_data->ima_use_appraise_tcb = true;
 		else if (strcmp(p, "secure_boot") == 0)
-			ima_use_secure_boot = true;
+			setup_data->ima_use_secure_boot = true;
 		else if (strcmp(p, "fail_securely") == 0)
-			ima_fail_unverifiable_sigs = true;
+			*fail_unverifiable_sigs = true;
 	}
 
 	return 1;
 }
+
+static int __init policy_setup(char *str)
+{
+	return ima_policy_setup(str, &init_policy_setup_data,
+				&ima_fail_unverifiable_sigs);
+}
 __setup("ima_policy=", policy_setup);
 
 int ima_default_appraise_policy_setup(const char *str,
 				      struct ima_policy_setup_data *setup_data)
 {
-	/* Currently unused. It will be implemented after namespacing ima
-	 * policy, when global variables are removed.
-	 */
+	setup_data->ima_use_appraise_tcb = true;
 	return 1;
 }
 
 static int __init default_appraise_policy_setup(char *str)
 {
-	ima_use_appraise_tcb = true;
-	return 1;
+	return ima_default_appraise_policy_setup(str, &init_policy_setup_data);
 }
 __setup("ima_appraise_tcb", default_appraise_policy_setup);
 
@@ -407,9 +390,11 @@ static bool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry)
 static void ima_lsm_update_rules(void)
 {
 	struct ima_rule_entry *entry, *e;
+	struct ima_namespace *ima_ns = get_current_ns();
 	int result;
 
-	list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
+	list_for_each_entry_safe(entry, e,
+				 &ima_ns->policy_data->ima_policy_rules, list) {
 		if (!ima_rule_contains_lsm_cond(entry))
 			continue;
 
@@ -615,12 +600,13 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
 {
 	struct ima_rule_entry *entry;
 	int action = 0, actmask = flags | (flags << 1);
+	bool fail_unverifiable_sigs;
 
 	if (template_desc)
 		*template_desc = ima_template_desc_current();
 
 	rcu_read_lock();
-	list_for_each_entry_rcu(entry, ima_rules, list) {
+	list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, list) {
 
 		if (!(entry->action & actmask))
 			continue;
@@ -635,7 +621,10 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
 		if (entry->action & IMA_APPRAISE) {
 			action |= get_subaction(entry, func);
 			action &= ~IMA_HASH;
-			if (ima_fail_unverifiable_sigs)
+			fail_unverifiable_sigs = (ima_ns == &init_ima_ns) ?
+				ima_fail_unverifiable_sigs :
+				ima_ns->policy_data->ima_fail_unverifiable_sigs;
+			if (fail_unverifiable_sigs)
 				action |= IMA_FAIL_UNVERIFIABLE_SIGS;
 		}
 
@@ -670,14 +659,15 @@ void ima_update_policy_flag(struct ima_namespace *ima_ns)
 {
 	struct ima_rule_entry *entry;
 
-	list_for_each_entry(entry, ima_rules, list) {
+	list_for_each_entry(entry, ima_ns->policy_data->ima_rules, list) {
 		if (entry->action & IMA_DO_MASK)
-			ima_policy_flag |= entry->action;
+			ima_ns->policy_data->ima_policy_flag |= entry->action;
 	}
 
-	ima_appraise |= (build_ima_appraise | temp_ima_appraise);
-	if (!ima_appraise)
-		ima_policy_flag &= ~IMA_APPRAISE;
+	ima_ns->policy_data->ima_appraise |=
+		(build_ima_appraise | ima_ns->policy_data->temp_ima_appraise);
+	if (!ima_ns->policy_data->ima_appraise)
+		ima_ns->policy_data->ima_policy_flag &= ~IMA_APPRAISE;
 }
 
 static int ima_appraise_flag(enum ima_hooks func)
@@ -693,7 +683,7 @@ static int ima_appraise_flag(enum ima_hooks func)
 	return 0;
 }
 
-static void add_rules(struct ima_policy_data *policy_data,
+static void add_rules(struct ima_namespace *ima_ns,
 		      struct ima_rule_entry *entries, int count,
 		      enum policy_rule_list policy_rule)
 {
@@ -702,8 +692,18 @@ static void add_rules(struct ima_policy_data *policy_data,
 	for (i = 0; i < count; i++) {
 		struct ima_rule_entry *entry;
 
-		if (policy_rule & IMA_DEFAULT_POLICY)
-			list_add_tail(&entries[i].list, &ima_default_rules);
+		if (policy_rule & IMA_DEFAULT_POLICY) {
+			entry = &entries[i];
+			if (ima_ns != &init_ima_ns) {
+				entry = kmemdup(&entries[i], sizeof(*entry),
+						GFP_KERNEL);
+				if (!entry)
+					continue;
+			}
+
+			list_add_tail(&entry->list,
+				      &ima_ns->policy_data->ima_default_rules);
+		}
 
 		if (policy_rule & IMA_CUSTOM_POLICY) {
 			entry = kmemdup(&entries[i], sizeof(*entry),
@@ -711,11 +711,12 @@ static void add_rules(struct ima_policy_data *policy_data,
 			if (!entry)
 				continue;
 
-			list_add_tail(&entry->list, &ima_policy_rules);
+			list_add_tail(&entry->list,
+				      &ima_ns->policy_data->ima_policy_rules);
 		}
 		if (entries[i].action == APPRAISE) {
 			if (entries != build_appraise_rules)
-				temp_ima_appraise |=
+				ima_ns->policy_data->temp_ima_appraise |=
 					ima_appraise_flag(entries[i].func);
 			else
 				build_ima_appraise |=
@@ -775,63 +776,58 @@ static int __init ima_init_arch_policy(void)
 void ima_init_ns_policy(struct ima_namespace *ima_ns,
 			const struct ima_policy_setup_data *setup_data)
 {
-	/* Set policy rules to the empty set of default rules. The rest will be
-	 * implemented after namespacing policy.
-	 */
-	ima_ns->policy_data->ima_rules =
-		&ima_ns->policy_data->ima_default_rules;
-}
+	int build_appraise_entries;
 
-/**
- * ima_init_policy - initialize the default measure rules.
- *
- * ima_rules points to either the ima_default_rules or the
- * the new ima_policy_rules.
- */
-void __init ima_init_policy(void)
-{
-	int build_appraise_entries, arch_entries;
+	ima_ns->policy_data->ima_appraise = setup_data->ima_appraise;
+
+	if (ima_ns == &init_ima_ns) {
+		/*
+		 * Based on runtime secure boot flags, insert arch specific
+		 * measurement and appraise rules requiring file signatures for
+		 * both the initial and custom policies, prior to other
+		 * appraise rules. (Highest priority)
+		 */
+		arch_entries_size = ima_init_arch_policy();
+		if (!arch_entries_size)
+			pr_info("No architecture policies found\n");
+
+		ima_ns->policy_data->ima_fail_unverifiable_sigs =
+			ima_fail_unverifiable_sigs;
+	}
 
 	/* if !ima_policy, we load NO default rules */
-	if (ima_policy)
-		add_rules(NULL,
-			  dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+	if (setup_data->ima_policy)
+		add_rules(ima_ns, dont_measure_rules,
+			  ARRAY_SIZE(dont_measure_rules),
 			  IMA_DEFAULT_POLICY);
 
-	switch (ima_policy) {
+	switch (setup_data->ima_policy) {
 	case ORIGINAL_TCB:
-		add_rules(NULL, original_measurement_rules,
+		add_rules(ima_ns, original_measurement_rules,
 			  ARRAY_SIZE(original_measurement_rules),
 			  IMA_DEFAULT_POLICY);
 		break;
 	case DEFAULT_TCB:
-		add_rules(NULL, default_measurement_rules,
+		add_rules(ima_ns, default_measurement_rules,
 			  ARRAY_SIZE(default_measurement_rules),
 			  IMA_DEFAULT_POLICY);
 	default:
 		break;
 	}
 
-	/*
-	 * Based on runtime secure boot flags, insert arch specific measurement
-	 * and appraise rules requiring file signatures for both the initial
-	 * and custom policies, prior to other appraise rules.
-	 * (Highest priority)
-	 */
-	arch_entries = ima_init_arch_policy();
-	if (!arch_entries)
-		pr_info("No architecture policies found\n");
-	else
-		add_rules(NULL, arch_policy_entry, arch_entries,
+	if (arch_entries_size)
+		add_rules(ima_ns,
+			  arch_policy_entry,
+			  arch_entries_size,
 			  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 
 	/*
 	 * Insert the builtin "secure_boot" policy rules requiring file
 	 * signatures, prior to other appraise rules.
 	 */
-	if (ima_use_secure_boot)
-		add_rules(NULL,
-			  secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
+	if (setup_data->ima_use_secure_boot)
+		add_rules(ima_ns, secure_boot_rules,
+			  ARRAY_SIZE(secure_boot_rules),
 			  IMA_DEFAULT_POLICY);
 
 	/*
@@ -841,29 +837,39 @@ void __init ima_init_policy(void)
 	 * rules, include either one or the other set of rules, but not both.
 	 */
 	build_appraise_entries = ARRAY_SIZE(build_appraise_rules);
-	if (build_appraise_entries) {
-		if (ima_use_secure_boot)
-			add_rules(NULL,
-				  build_appraise_rules, build_appraise_entries,
+	if (build_appraise_entries && (ima_ns == &init_ima_ns)) {
+		if (setup_data->ima_use_secure_boot)
+			add_rules(ima_ns, build_appraise_rules,
+				  build_appraise_entries,
 				  IMA_CUSTOM_POLICY);
 		else
-			add_rules(NULL,
-				  build_appraise_rules, build_appraise_entries,
+			add_rules(ima_ns, build_appraise_rules,
+				  build_appraise_entries,
 				  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 	}
 
-	if (ima_use_appraise_tcb)
-		add_rules(NULL, default_appraise_rules,
+	if (setup_data->ima_use_appraise_tcb)
+		add_rules(ima_ns, default_appraise_rules,
 			  ARRAY_SIZE(default_appraise_rules),
 			  IMA_DEFAULT_POLICY);
 
-	ima_update_policy_flag(NULL);
+	ima_ns->policy_data->ima_rules =
+		&ima_ns->policy_data->ima_default_rules;
+	ima_update_policy_flag(ima_ns);
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules for the initial ima ns
+ */
+void __init ima_init_policy(void)
+{
+	ima_init_ns_policy(&init_ima_ns, &init_policy_setup_data);
 }
 
 /* Make sure we have a valid policy, at least containing some rules. */
 int ima_check_policy(const struct ima_namespace *ima_ns)
 {
-	if (list_empty(&ima_temp_rules))
+	if (list_empty(&ima_ns->policy_data->ima_temp_rules))
 		return -EINVAL;
 	return 0;
 }
@@ -881,14 +887,18 @@ int ima_check_policy(const struct ima_namespace *ima_ns)
  */
 void ima_update_policy(void)
 {
-	struct list_head *policy = &ima_policy_rules;
+	/* Update only the current ima namespace */
+	struct ima_namespace *ima_ns = get_current_ns();
+	struct list_head *policy = &ima_ns->policy_data->ima_policy_rules;
 
-	list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu);
+	list_splice_tail_init_rcu(&ima_ns->policy_data->ima_temp_rules,
+				  policy, synchronize_rcu);
 
-	if (ima_rules != policy) {
-		ima_policy_flag = 0;
-		ima_rules = policy;
+	if (ima_ns->policy_data->ima_rules != policy) {
+		ima_ns->policy_data->ima_policy_flag = 0;
+		ima_ns->policy_data->ima_rules = policy;
 
+#ifndef CONFIG_IMA_NS
 		/*
 		 * IMA architecture specific policy rules are specified
 		 * as strings and converted to an array of ima_entry_rules
@@ -896,8 +906,9 @@ void ima_update_policy(void)
 		 * architecture specific rules stored as an array.
 		 */
 		kfree(arch_policy_entry);
+#endif
 	}
-	ima_update_policy_flag(NULL);
+	ima_update_policy_flag(ima_ns);
 
 	/* Custom IMA policy has been loaded */
 	ima_process_queued_keys();
@@ -960,6 +971,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
 			     substring_t *args, int lsm_rule, int audit_type)
 {
 	int result;
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	if (entry->lsm[lsm_rule].rule)
 		return -EINVAL;
@@ -976,7 +988,8 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
 		pr_warn("rule for LSM \'%s\' is undefined\n",
 			entry->lsm[lsm_rule].args_p);
 
-		if (ima_rules == &ima_default_rules) {
+		if (ima_ns->policy_data->ima_rules ==
+		    &ima_ns->policy_data->ima_default_rules) {
 			kfree(entry->lsm[lsm_rule].args_p);
 			entry->lsm[lsm_rule].args_p = NULL;
 			result = -EINVAL;
@@ -1137,6 +1150,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 	struct ima_template_desc *template_desc;
 	int result = 0;
 	size_t keyrings_len;
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
 				       AUDIT_INTEGRITY_POLICY_RULE);
@@ -1506,7 +1520,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 	if (!result && !ima_validate_rule(entry))
 		result = -EINVAL;
 	else if (entry->action == APPRAISE)
-		temp_ima_appraise |= ima_appraise_flag(entry->func);
+		ima_ns->policy_data->temp_ima_appraise |=
+			ima_appraise_flag(entry->func);
 
 	if (!result && entry->flags & IMA_MODSIG_ALLOWED) {
 		template_desc = entry->template ? entry->template :
@@ -1533,6 +1548,8 @@ ssize_t ima_parse_add_rule(char *rule)
 	struct ima_rule_entry *entry;
 	ssize_t result, len;
 	int audit_info = 0;
+	/* Add rules only to the current ima namespace */
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	p = strsep(&rule, "\n");
 	len = strlen(p) + 1;
@@ -1559,7 +1576,7 @@ ssize_t ima_parse_add_rule(char *rule)
 		return result;
 	}
 
-	list_add_tail(&entry->list, &ima_temp_rules);
+	list_add_tail(&entry->list, &ima_ns->policy_data->ima_temp_rules);
 
 	return len;
 }
@@ -1571,15 +1588,51 @@ ssize_t ima_parse_add_rule(char *rule)
  * ima_delete_rules() at a time.
  */
 void ima_delete_rules(void)
+{
+	/* Delete rules only from the current ima namespace */
+	struct ima_namespace *ima_ns = get_current_ns();
+	struct ima_rule_entry *entry, *tmp;
+
+	ima_ns->policy_data->temp_ima_appraise = 0;
+	list_for_each_entry_safe(entry, tmp,
+				 &ima_ns->policy_data->ima_temp_rules, list) {
+		list_del(&entry->list);
+		ima_free_rule(entry);
+	}
+}
+
+#ifdef CONFIG_IMA_NS
+/**
+ * ima_delete_ns_rules - delete policy rules and free the memory
+ * @policy_data: a pointer to the policy data of the given namespace
+ * @is_root_ns: indicates if the namespace being cleaned up is the root
+ * namespace
+ *
+ * This function should be called only for the inactive namespace, when it is
+ * being destroyed.
+ */
+void ima_delete_ns_rules(struct ima_policy_data *policy_data,
+			 bool is_root_ns)
 {
 	struct ima_rule_entry *entry, *tmp;
 
-	temp_ima_appraise = 0;
-	list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
+	/* no locks necessary, namespace is inactive */
+	list_for_each_entry_safe(entry, tmp,
+				 &policy_data->ima_policy_rules, list) {
 		list_del(&entry->list);
 		ima_free_rule(entry);
 	}
+
+	if (!is_root_ns) {
+		list_for_each_entry_safe(entry, tmp,
+					 &policy_data->ima_default_rules,
+					 list) {
+			list_del(&entry->list);
+			ima_free_rule(entry);
+		}
+	}
 }
+#endif
 
 #define __ima_hook_stringify(func, str)	(#func),
 
@@ -1603,9 +1656,10 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
 {
 	loff_t l = *pos;
 	struct ima_rule_entry *entry;
+	struct ima_namespace *ima_ns = get_current_ns();
 
 	rcu_read_lock();
-	list_for_each_entry_rcu(entry, ima_rules, list) {
+	list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, list) {
 		if (!l--) {
 			rcu_read_unlock();
 			return entry;
@@ -1617,6 +1671,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
 
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
 	struct ima_rule_entry *entry = v;
 
 	rcu_read_lock();
@@ -1624,7 +1679,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 	rcu_read_unlock();
 	(*pos)++;
 
-	return (&entry->list == ima_rules) ? NULL : entry;
+	return (&entry->list == ima_ns->policy_data->ima_rules) ? NULL : entry;
 }
 
 void ima_policy_stop(struct seq_file *m, void *v)
@@ -1809,6 +1864,7 @@ int ima_policy_show(struct seq_file *m, void *v)
  */
 bool ima_appraise_signature(enum kernel_read_file_id id)
 {
+	struct ima_namespace *ima_ns = get_current_ns();
 	struct ima_rule_entry *entry;
 	bool found = false;
 	enum ima_hooks func;
@@ -1819,7 +1875,7 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
 	func = read_idmap[id] ?: FILE_CHECK;
 
 	rcu_read_lock();
-	list_for_each_entry_rcu(entry, ima_rules, list) {
+	list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, list) {
 		if (entry->action != APPRAISE)
 			continue;
 
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 34ca54ba52b7..542cbe894a99 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -110,8 +110,11 @@ bool ima_queue_key(struct key *keyring, const void *payload,
 	if (!entry)
 		return false;
 
+	/* Queued keys will be processed according to the root IMA namespace
+	 * policy, therefore allow queueing only for the root namespace.
+	 */
 	mutex_lock(&ima_keys_lock);
-	if (!ima_process_keys) {
+	if (!ima_process_keys && (get_current_ns() == &init_ima_ns)) {
 		list_add_tail(&entry->list, &ima_keys);
 		queued = true;
 	}
@@ -158,12 +161,15 @@ void ima_process_queued_keys(void)
 
 	list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
 		if (!timer_expired)
+			/* Queued keys are always measured according to the
+			 * initial namespace policy.
+			 */
 			process_buffer_measurement(NULL, entry->payload,
 						   entry->payload_len,
 						   entry->keyring_name,
 						   KEY_CHECK, 0,
 						   entry->keyring_name,
-						   NULL);
+						   &init_ima_ns);
 		list_del(&entry->list);
 		ima_free_key_entry(entry);
 	}
diff --git a/security/security.c b/security/security.c
index 70a7ad357bc6..9eb78909cc03 100644
--- a/security/security.c
+++ b/security/security.c
@@ -988,7 +988,7 @@ static void inode_free_by_rcu(struct rcu_head *head)
 
 void security_inode_free(struct inode *inode)
 {
-	integrity_inode_free(inode);
+	ima_inode_free(inode);
 	call_void_hook(inode_free_security, inode);
 	/*
 	 * The inode may still be referenced in a path walk and
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 08/30] ima: Add integrity inode related data to the ima namespace
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add an iint tree to the ima namespace. Each namespace should track
operations on its objects separately. Per namespace iint tree is not
yet used, it will be done in the following patches.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h               |  1 +
 security/integrity/ima/ima_init.c |  1 +
 security/integrity/ima/ima_ns.c   | 17 +++++++++++++++--
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 3fd3746a0dee..9069aafd905f 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -190,6 +190,7 @@ struct ima_namespace {
 	atomic_t inactive; /* set only when ns is added to the cleanup list */
 	bool frozen;
 	struct ima_policy_data *policy_data;
+	struct integrity_iint_tree *iint_tree;
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index ea5ff42eb7fe..be1afc42fdf5 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -34,6 +34,7 @@ struct ima_namespace init_ima_ns = {
 #endif
 	.frozen = true,
 	.policy_data = &init_policy_data,
+	.iint_tree = &init_iint_tree,
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 1aeb9cfeb3a2..226a53279f71 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -56,11 +56,18 @@ static struct ima_namespace *ima_ns_alloc(void)
 	ima_ns->policy_data = kzalloc(sizeof(struct ima_policy_data),
 				      GFP_KERNEL);
 	if (!ima_ns->policy_data)
-		goto out_free;
+		goto ns_free;
+
+	ima_ns->iint_tree = kzalloc(sizeof(struct integrity_iint_tree),
+				    GFP_KERNEL);
+	if (!ima_ns->iint_tree)
+		goto policy_free;
 
 	return ima_ns;
 
-out_free:
+policy_free:
+	kfree(ima_ns->policy_data);
+ns_free:
 	kfree(ima_ns);
 out:
 	return NULL;
@@ -120,6 +127,9 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	ns->ucounts = ucounts;
 	ns->frozen = false;
 
+	rwlock_init(&ns->iint_tree->lock);
+	ns->iint_tree->root = RB_ROOT;
+
 	INIT_LIST_HEAD(&ns->policy_data->ima_default_rules);
 	INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules);
 	INIT_LIST_HEAD(&ns->policy_data->ima_temp_rules);
@@ -127,6 +137,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	return ns;
 
 fail_free:
+	kfree(ns->iint_tree);
 	kfree(ns->policy_data);
 	kfree(ns);
 fail_dec:
@@ -173,6 +184,8 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	dec_ima_namespaces(ns->ucounts);
 	put_user_ns(ns->user_ns);
 	ns_free_inum(&ns->ns);
+	integrity_iint_tree_free(ns->iint_tree);
+	kfree(ns->iint_tree);
 	kfree(ns->policy_data);
 	kfree(ns);
 }
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 06/30] ima: Add ima namespace to the ima subsystem APIs
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Add ima namespace pointer to the input parameters of the relevant
functions. This is a preparation for the policy namespacing, more
functions may be modified later, when other aspects of the ima are
namespaced.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/ima.h                          |  4 +--
 security/integrity/ima/ima.h                 | 25 ++++++++-----
 security/integrity/ima/ima_api.c             |  6 ++--
 security/integrity/ima/ima_appraise.c        | 16 +++++----
 security/integrity/ima/ima_asymmetric_keys.c |  3 +-
 security/integrity/ima/ima_fs.c              |  2 +-
 security/integrity/ima/ima_main.c            | 38 +++++++++++---------
 security/integrity/ima/ima_policy.c          | 36 +++++++++++--------
 security/integrity/ima/ima_queue_keys.c      |  3 +-
 9 files changed, 80 insertions(+), 53 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index d61c9c21ffb9..3fd3746a0dee 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -140,13 +140,13 @@ static inline void ima_post_key_create_or_update(struct key *keyring,
 #endif  /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
 
 #ifdef CONFIG_IMA_APPRAISE
-extern bool is_ima_appraise_enabled(void);
+extern bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns);
 extern void ima_inode_post_setattr(struct dentry *dentry);
 extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
 		       const void *xattr_value, size_t xattr_value_len);
 extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
 #else
-static inline bool is_ima_appraise_enabled(void)
+static inline bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
 {
 	return 0;
 }
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 4872f193f7a3..7d522fdab0d8 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -264,7 +264,8 @@ static inline void ima_process_queued_keys(void) {}
 int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
 		   int mask, enum ima_hooks func, int *pcr,
 		   struct ima_template_desc **template_desc,
-		   const char *keyring);
+		   const char *keyring,
+		   const struct ima_namespace *ima_ns);
 int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
 			    struct file *file, void *buf, loff_t size,
@@ -276,7 +277,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
 			   struct ima_template_desc *template_desc);
 void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 				const char *eventname, enum ima_hooks func,
-				int pcr, const char *keyring);
+				int pcr, const char *keyring,
+				struct ima_namespace *ima_ns);
 void ima_audit_measurement(struct integrity_iint_cache *iint,
 			   const unsigned char *filename);
 int ima_alloc_init_template(struct ima_event_data *event_data,
@@ -292,15 +294,16 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
 int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
 		     enum ima_hooks func, int mask, int flags, int *pcr,
 		     struct ima_template_desc **template_desc,
-		     const char *keyring);
+		     const char *keyring,
+		     const struct ima_namespace *ima_ns);
 void ima_init_policy(void);
 void ima_init_ns_policy(struct ima_namespace *ima_ns,
 			const struct ima_policy_setup_data *policy_setup_data);
 void ima_update_policy(void);
-void ima_update_policy_flag(void);
+void ima_update_policy_flag(struct ima_namespace *ima_ns);
 ssize_t ima_parse_add_rule(char *);
 void ima_delete_rules(void);
-int ima_check_policy(void);
+int ima_check_policy(const struct ima_namespace *ima_ns);
 void *ima_policy_start(struct seq_file *m, loff_t *pos);
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
 void ima_policy_stop(struct seq_file *m, void *v);
@@ -327,13 +330,15 @@ int ima_default_appraise_setup(const char *str,
 
 #ifdef CONFIG_IMA_APPRAISE
 int ima_check_blacklist(struct integrity_iint_cache *iint,
-			const struct modsig *modsig, int pcr);
+			const struct modsig *modsig, int pcr,
+			struct ima_namespace *ima_ns);
 int ima_appraise_measurement(enum ima_hooks func,
 			     struct integrity_iint_cache *iint,
 			     struct file *file, const unsigned char *filename,
 			     struct evm_ima_xattr_data *xattr_value,
 			     int xattr_len, const struct modsig *modsig);
-int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
+int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
+		      struct ima_namespace *ima_ns);
 void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
 enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
 					   enum ima_hooks func);
@@ -344,7 +349,8 @@ int ima_read_xattr(struct dentry *dentry,
 
 #else
 static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
-				      const struct modsig *modsig, int pcr)
+				      const struct modsig *modsig, int pcr,
+				      struct ima_namespace *ima_ns)
 {
 	return 0;
 }
@@ -361,7 +367,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
 }
 
 static inline int ima_must_appraise(struct inode *inode, int mask,
-				    enum ima_hooks func)
+				    enum ima_hooks func,
+				    struct ima_namespace *ima_ns)
 {
 	return 0;
 }
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 4f39fb93f278..8b41183200e8 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -171,6 +171,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
  * @pcr: pointer filled in if matched measure policy sets pcr=
  * @template_desc: pointer filled in if matched measure policy sets template=
  * @keyring: keyring name used to determine the action
+ * @ima_ns: ima namespace whose policy data will be used
  *
  * The policy is defined in terms of keypairs:
  *		subj=, obj=, type=, func=, mask=, fsmagic=
@@ -186,14 +187,15 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
 		   int mask, enum ima_hooks func, int *pcr,
 		   struct ima_template_desc **template_desc,
-		   const char *keyring)
+		   const char *keyring,
+		   const struct ima_namespace *ima_ns)
 {
 	int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
 
 	flags &= ima_policy_flag;
 
 	return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
-				template_desc, keyring);
+				template_desc, keyring, ima_ns);
 }
 
 /*
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 0632d3881611..9388ff88ca4d 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -48,10 +48,11 @@ __setup("ima_appraise=", default_appraise_setup);
 
 /*
  * is_ima_appraise_enabled - return appraise status
+ * @ima_ns: pointer to the ima namespace being checked
  *
  * Only return enabled, if not in ima_appraise="fix" or "log" modes.
  */
-bool is_ima_appraise_enabled(void)
+bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
 {
 	return ima_appraise & IMA_APPRAISE_ENFORCE;
 }
@@ -61,7 +62,8 @@ bool is_ima_appraise_enabled(void)
  *
  * Return 1 to appraise or hash
  */
-int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
+int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
+		      struct ima_namespace *ima_ns)
 {
 	u32 secid;
 
@@ -70,7 +72,8 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
 
 	security_task_getsecid(current, &secid);
 	return ima_match_policy(inode, current_cred(), secid, func, mask,
-				IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL);
+				IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL,
+				NULL);
 }
 
 static int ima_fix_xattr(struct dentry *dentry,
@@ -328,7 +331,8 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
  * Returns -EPERM if the hash is blacklisted.
  */
 int ima_check_blacklist(struct integrity_iint_cache *iint,
-			const struct modsig *modsig, int pcr)
+			const struct modsig *modsig, int pcr,
+			struct ima_namespace *ima_ns)
 {
 	enum hash_algo hash_algo;
 	const u8 *digest = NULL;
@@ -345,7 +349,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
 		if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
 			process_buffer_measurement(NULL, digest, digestsize,
 						   "blacklisted-hash", NONE,
-						   pcr, NULL);
+						   pcr, NULL, NULL);
 	}
 
 	return rc;
@@ -511,7 +515,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
 	    || !(inode->i_opflags & IOP_XATTR))
 		return;
 
-	action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
+	action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR, NULL);
 	if (!action)
 		__vfs_removexattr(dentry, XATTR_NAME_IMA);
 	iint = integrity_iint_find(inode);
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 1c68c500c26f..58aa56b0422d 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -60,5 +60,6 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 	 */
 	process_buffer_measurement(NULL, payload, payload_len,
 				   keyring->description, KEY_CHECK, 0,
-				   keyring->description);
+				   keyring->description,
+				   NULL);
 }
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e3fcad871861..97aadee7e68e 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -410,7 +410,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
 
-	if (valid_policy && ima_check_policy() < 0) {
+	if (valid_policy && ima_check_policy(NULL) < 0) {
 		cause = "failed";
 		valid_policy = 0;
 	}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index c7e29277b953..196fa2bd490d 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -109,7 +109,8 @@ static void ima_rdwr_violation_check(struct file *file,
 				     int must_measure,
 				     char **pathbuf,
 				     const char **pathname,
-				     char *filename)
+				     char *filename,
+				     struct ima_namespace *ima_ns)
 {
 	struct inode *inode = file_inode(file);
 	fmode_t mode = file->f_mode;
@@ -215,7 +216,8 @@ void ima_file_free(struct file *file)
 
 static int process_measurement(struct file *file, const struct cred *cred,
 			       u32 secid, char *buf, loff_t size, int mask,
-			       enum ima_hooks func)
+			       enum ima_hooks func,
+			       struct ima_namespace *ima_ns)
 {
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
@@ -239,7 +241,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	 * Included is the appraise submask.
 	 */
 	action = ima_get_action(inode, cred, secid, mask, func, &pcr,
-				&template_desc, NULL);
+				&template_desc, NULL, ima_ns);
 	violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
 			   (ima_policy_flag & IMA_MEASURE));
 	if (!action && !violation_check)
@@ -261,7 +263,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 
 	if (!rc && violation_check)
 		ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
-					 &pathbuf, &pathname, filename);
+					 &pathbuf, &pathname, filename, ima_ns);
 
 	inode_unlock(inode);
 
@@ -359,7 +361,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 				      xattr_value, xattr_len, modsig, pcr,
 				      template_desc);
 	if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
-		rc = ima_check_blacklist(iint, modsig, pcr);
+		rc = ima_check_blacklist(iint, modsig, pcr, ima_ns);
 		if (rc != -EPERM) {
 			inode_lock(inode);
 			rc = ima_appraise_measurement(func, iint, file,
@@ -413,7 +415,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
 	if (file && (prot & PROT_EXEC)) {
 		security_task_getsecid(current, &secid);
 		return process_measurement(file, current_cred(), secid, NULL,
-					   0, MAY_EXEC, MMAP_CHECK);
+					   0, MAY_EXEC, MMAP_CHECK, NULL);
 	}
 
 	return 0;
@@ -452,7 +454,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 	security_task_getsecid(current, &secid);
 	inode = file_inode(vma->vm_file);
 	action = ima_get_action(inode, current_cred(), secid, MAY_EXEC,
-				MMAP_CHECK, &pcr, &template, 0);
+				MMAP_CHECK, &pcr, &template, 0, NULL);
 
 	/* Is the mmap'ed file in policy? */
 	if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
@@ -491,13 +493,13 @@ int ima_bprm_check(struct linux_binprm *bprm)
 
 	security_task_getsecid(current, &secid);
 	ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
-				  MAY_EXEC, BPRM_CHECK);
+				  MAY_EXEC, BPRM_CHECK, NULL);
 	if (ret)
 		return ret;
 
 	security_cred_getsecid(bprm->cred, &secid);
 	return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
-				   MAY_EXEC, CREDS_CHECK);
+				   MAY_EXEC, CREDS_CHECK, NULL);
 }
 
 /**
@@ -517,7 +519,7 @@ int ima_file_check(struct file *file, int mask)
 	security_task_getsecid(current, &secid);
 	return process_measurement(file, current_cred(), secid, NULL, 0,
 				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
-					   MAY_APPEND), FILE_CHECK);
+					   MAY_APPEND), FILE_CHECK, NULL);
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
@@ -583,7 +585,7 @@ void ima_post_create_tmpfile(struct inode *inode)
 	struct integrity_iint_cache *iint;
 	int must_appraise;
 
-	must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+	must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
 	if (!must_appraise)
 		return;
 
@@ -610,7 +612,7 @@ void ima_post_path_mknod(struct dentry *dentry)
 	struct inode *inode = dentry->d_inode;
 	int must_appraise;
 
-	must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+	must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
 	if (!must_appraise)
 		return;
 
@@ -697,7 +699,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 	func = read_idmap[read_id] ?: FILE_CHECK;
 	security_task_getsecid(current, &secid);
 	return process_measurement(file, current_cred(), secid, buf, size,
-				   MAY_READ, func);
+				   MAY_READ, func, NULL);
 }
 
 /**
@@ -764,7 +766,8 @@ int ima_load_data(enum kernel_load_data_id id)
  */
 void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 				const char *eventname, enum ima_hooks func,
-				int pcr, const char *keyring)
+				int pcr, const char *keyring,
+				struct ima_namespace *ima_ns)
 {
 	int ret = 0;
 	const char *audit_cause = "ENOMEM";
@@ -796,7 +799,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
 	if (func) {
 		security_task_getsecid(current, &secid);
 		action = ima_get_action(inode, current_cred(), secid, 0, func,
-					&pcr, &template, keyring);
+					&pcr, &template, keyring, NULL);
 		if (!(action & IMA_MEASURE))
 			return;
 	}
@@ -868,7 +871,8 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 		return;
 
 	process_buffer_measurement(file_inode(f.file), buf, size,
-				   "kexec-cmdline", KEXEC_CMDLINE, 0, NULL);
+				   "kexec-cmdline", KEXEC_CMDLINE, 0, NULL,
+				   NULL);
 	fdput(f);
 }
 
@@ -897,7 +901,7 @@ static int __init init_ima(void)
 		pr_warn("Couldn't register LSM notifier, error %d\n", error);
 
 	if (!error)
-		ima_update_policy_flag();
+		ima_update_policy_flag(&init_ima_ns);
 
 	return error;
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 403854b18ef2..12f9dcf73c83 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -598,6 +598,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * @template_desc: the template that should be used for this rule
  * @keyring: the keyring name, if given, to be used to check in the policy.
  *           keyring can be NULL if func is anything other than KEY_CHECK.
+ * @ima_ns: IMA namespace whose policies are being checked
  *
  * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
  * conditions.
@@ -609,7 +610,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
 int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
 		     enum ima_hooks func, int mask, int flags, int *pcr,
 		     struct ima_template_desc **template_desc,
-		     const char *keyring)
+		     const char *keyring,
+		     const struct ima_namespace *ima_ns)
 {
 	struct ima_rule_entry *entry;
 	int action = 0, actmask = flags | (flags << 1);
@@ -662,8 +664,9 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
  * loaded policy.  Based on this flag, the decision to short circuit
  * out of a function or not call the function in the first place
  * can be made earlier.
+ * @ima_ns: pointer to the ima namespace whose policy flag is updated
  */
-void ima_update_policy_flag(void)
+void ima_update_policy_flag(struct ima_namespace *ima_ns)
 {
 	struct ima_rule_entry *entry;
 
@@ -690,7 +693,8 @@ static int ima_appraise_flag(enum ima_hooks func)
 	return 0;
 }
 
-static void add_rules(struct ima_rule_entry *entries, int count,
+static void add_rules(struct ima_policy_data *policy_data,
+		      struct ima_rule_entry *entries, int count,
 		      enum policy_rule_list policy_rule)
 {
 	int i = 0;
@@ -790,17 +794,18 @@ void __init ima_init_policy(void)
 
 	/* if !ima_policy, we load NO default rules */
 	if (ima_policy)
-		add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+		add_rules(NULL,
+			  dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
 			  IMA_DEFAULT_POLICY);
 
 	switch (ima_policy) {
 	case ORIGINAL_TCB:
-		add_rules(original_measurement_rules,
+		add_rules(NULL, original_measurement_rules,
 			  ARRAY_SIZE(original_measurement_rules),
 			  IMA_DEFAULT_POLICY);
 		break;
 	case DEFAULT_TCB:
-		add_rules(default_measurement_rules,
+		add_rules(NULL, default_measurement_rules,
 			  ARRAY_SIZE(default_measurement_rules),
 			  IMA_DEFAULT_POLICY);
 	default:
@@ -817,7 +822,7 @@ void __init ima_init_policy(void)
 	if (!arch_entries)
 		pr_info("No architecture policies found\n");
 	else
-		add_rules(arch_policy_entry, arch_entries,
+		add_rules(NULL, arch_policy_entry, arch_entries,
 			  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 
 	/*
@@ -825,7 +830,8 @@ void __init ima_init_policy(void)
 	 * signatures, prior to other appraise rules.
 	 */
 	if (ima_use_secure_boot)
-		add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
+		add_rules(NULL,
+			  secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
 			  IMA_DEFAULT_POLICY);
 
 	/*
@@ -837,23 +843,25 @@ void __init ima_init_policy(void)
 	build_appraise_entries = ARRAY_SIZE(build_appraise_rules);
 	if (build_appraise_entries) {
 		if (ima_use_secure_boot)
-			add_rules(build_appraise_rules, build_appraise_entries,
+			add_rules(NULL,
+				  build_appraise_rules, build_appraise_entries,
 				  IMA_CUSTOM_POLICY);
 		else
-			add_rules(build_appraise_rules, build_appraise_entries,
+			add_rules(NULL,
+				  build_appraise_rules, build_appraise_entries,
 				  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 	}
 
 	if (ima_use_appraise_tcb)
-		add_rules(default_appraise_rules,
+		add_rules(NULL, default_appraise_rules,
 			  ARRAY_SIZE(default_appraise_rules),
 			  IMA_DEFAULT_POLICY);
 
-	ima_update_policy_flag();
+	ima_update_policy_flag(NULL);
 }
 
 /* Make sure we have a valid policy, at least containing some rules. */
-int ima_check_policy(void)
+int ima_check_policy(const struct ima_namespace *ima_ns)
 {
 	if (list_empty(&ima_temp_rules))
 		return -EINVAL;
@@ -889,7 +897,7 @@ void ima_update_policy(void)
 		 */
 		kfree(arch_policy_entry);
 	}
-	ima_update_policy_flag();
+	ima_update_policy_flag(NULL);
 
 	/* Custom IMA policy has been loaded */
 	ima_process_queued_keys();
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 69a8626a35c0..34ca54ba52b7 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -162,7 +162,8 @@ void ima_process_queued_keys(void)
 						   entry->payload_len,
 						   entry->keyring_name,
 						   KEY_CHECK, 0,
-						   entry->keyring_name);
+						   entry->keyring_name,
+						   NULL);
 		list_del(&entry->list);
 		ima_free_key_entry(entry);
 	}
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 07/30] ima: Extend the APIs in the integrity subsystem
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

Inode integrity cache will be maintained per ima namespace. Add new
functions that allow to specify the iint tree to use.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/integrity.h      |  31 ++++++++
 security/integrity/iint.c      | 126 ++++++++++++++++++++++++++-------
 security/integrity/integrity.h |  11 +++
 3 files changed, 144 insertions(+), 24 deletions(-)

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2271939c5c31..5019fedaa17a 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -8,6 +8,10 @@
 #define _LINUX_INTEGRITY_H
 
 #include <linux/fs.h>
+#include <linux/rwlock_types.h>
+
+struct rb_root;
+struct integrity_iint_tree;
 
 enum integrity_status {
 	INTEGRITY_PASS = 0,
@@ -21,8 +25,15 @@ enum integrity_status {
 /* List of EVM protected security xattrs */
 #ifdef CONFIG_INTEGRITY
 extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
+extern struct integrity_iint_cache *integrity_inode_rb_get(struct
+							   integrity_iint_tree
+							   *iint_tree,
+							   struct inode *inode);
 extern void integrity_inode_free(struct inode *inode);
+extern void integrity_inode_rb_free(struct integrity_iint_tree *iint_tree,
+				    struct inode *inode);
 extern void __init integrity_load_keys(void);
+extern void integrity_iint_tree_free(struct integrity_iint_tree *iint_tree);
 
 #else
 static inline struct integrity_iint_cache *
@@ -31,14 +42,34 @@ static inline struct integrity_iint_cache *
 	return NULL;
 }
 
+static inline struct integrity_iint_cache *
+				integrity_inode_rb_get(struct
+						       integrity_iint_tree
+						       *iint_tree,
+						       struct inode *inode)
+{
+	return NULL;
+}
+
 static inline void integrity_inode_free(struct inode *inode)
 {
 	return;
 }
 
+static inline void integrity_inode_rb_free(struct integrity_iint_tree
+					   *iint_tree,
+					   struct inode *inode)
+{
+}
+
 static inline void integrity_load_keys(void)
 {
 }
+
+static inline void integrity_iint_tree_free(struct integrity_iint_tree
+					    *iint_tree)
+{
+}
 #endif /* CONFIG_INTEGRITY */
 
 #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 1d20003243c3..34a36f298f92 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -21,19 +21,29 @@
 #include <linux/lsm_hooks.h>
 #include "integrity.h"
 
-static struct rb_root integrity_iint_tree = RB_ROOT;
-static DEFINE_RWLOCK(integrity_iint_lock);
+struct integrity_iint_tree init_iint_tree = {
+	.lock = __RW_LOCK_UNLOCKED(init_iint_tree.lock),
+	.root = RB_ROOT
+};
+
 static struct kmem_cache *iint_cache __read_mostly;
 
 struct dentry *integrity_dir;
 
 /*
- * __integrity_iint_find - return the iint associated with an inode
+ * __integrity_iint_rb_find - return the iint associated with an inode
+ * @iint_rb_root: pointer to the root of the iint tree
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
  */
-static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+static struct integrity_iint_cache *
+				__integrity_iint_rb_find(const struct rb_root
+							 *iint_rb_root,
+							 const struct inode
+							 *inode)
 {
 	struct integrity_iint_cache *iint;
-	struct rb_node *n = integrity_iint_tree.rb_node;
+	struct rb_node *n = iint_rb_root->rb_node;
 
 	while (n) {
 		iint = rb_entry(n, struct integrity_iint_cache, rb_node);
@@ -52,22 +62,37 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
 }
 
 /*
- * integrity_iint_find - return the iint associated with an inode
+ * integrity_iint_rb_find - return the iint associated with an inode
+ * @iint_tree: pointer to the iint tree root node and the associated lock
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
  */
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree
+						    *iint_tree,
+						    const struct inode *inode)
 {
 	struct integrity_iint_cache *iint;
 
 	if (!IS_IMA(inode))
 		return NULL;
 
-	read_lock(&integrity_iint_lock);
-	iint = __integrity_iint_find(inode);
-	read_unlock(&integrity_iint_lock);
+	read_lock(&iint_tree->lock);
+	iint = __integrity_iint_rb_find(&iint_tree->root, inode);
+	read_unlock(&iint_tree->lock);
 
 	return iint;
 }
 
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+	return integrity_iint_rb_find(&init_iint_tree, inode);
+}
+
 static void iint_free(struct integrity_iint_cache *iint)
 {
 	kfree(iint->ima_hash);
@@ -86,19 +111,42 @@ static void iint_free(struct integrity_iint_cache *iint)
 }
 
 /**
- * integrity_inode_get - find or allocate an iint associated with an inode
+ * integrity_iint_tree_free - traverse the tree and free all nodes
+ * @iint_tree: pointer to the iint tree root node and the associated lock
+ *
+ * The tree cannot be in use. This function should be called only from the
+ * destructor when no locks are required.
+ */
+void integrity_iint_tree_free(struct integrity_iint_tree *iint_tree)
+{
+	struct rb_root *root = &iint_tree->root;
+	struct integrity_iint_cache *iint, *tmp;
+
+	rbtree_postorder_for_each_entry_safe(iint, tmp, root, rb_node) {
+		iint_free(iint);
+	}
+
+	iint_tree->root = RB_ROOT;
+}
+
+/**
+ * integrity_inode_rb_get - find or allocate an iint associated with an inode
+ * @iint_tree: pointer to the iint tree root node and the associated lock
  * @inode: pointer to the inode
- * @return: allocated iint
+ * @return: pointer to the existing iint if found, pointer to the allocated iint
+ * if it didn't exist, NULL in case of error
  *
  * Caller must lock i_mutex
  */
-struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
+struct integrity_iint_cache *integrity_inode_rb_get(struct integrity_iint_tree
+						    *iint_tree,
+						    struct inode *inode)
 {
 	struct rb_node **p;
 	struct rb_node *node, *parent = NULL;
 	struct integrity_iint_cache *iint, *test_iint;
 
-	iint = integrity_iint_find(inode);
+	iint = integrity_iint_rb_find(iint_tree, inode);
 	if (iint)
 		return iint;
 
@@ -106,9 +154,9 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
 	if (!iint)
 		return NULL;
 
-	write_lock(&integrity_iint_lock);
+	write_lock(&iint_tree->lock);
 
-	p = &integrity_iint_tree.rb_node;
+	p = &iint_tree->root.rb_node;
 	while (*p) {
 		parent = *p;
 		test_iint = rb_entry(parent, struct integrity_iint_cache,
@@ -123,33 +171,63 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
 	node = &iint->rb_node;
 	inode->i_flags |= S_IMA;
 	rb_link_node(node, parent, p);
-	rb_insert_color(node, &integrity_iint_tree);
+	rb_insert_color(node, &iint_tree->root);
 
-	write_unlock(&integrity_iint_lock);
+	write_unlock(&iint_tree->lock);
 	return iint;
 }
 
 /**
- * integrity_inode_free - called on security_inode_free
+ * integrity_inode_get - find or allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ * @return: pointer to the existing iint if found, pointer to the allocated iint
+ * if it didn't exist, NULL in case of error
+ *
+ * Caller must lock i_mutex
+ */
+struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
+{
+	return integrity_inode_rb_get(&init_iint_tree, inode);
+}
+
+/**
+ * integrity_inode_rb_free - called on security_inode_free
+ * @iint_tree: pointer to the iint tree root node and the associated lock
  * @inode: pointer to the inode
  *
  * Free the integrity information(iint) associated with an inode.
  */
-void integrity_inode_free(struct inode *inode)
+void integrity_inode_rb_free(struct integrity_iint_tree *iint_tree,
+			     struct inode *inode)
 {
 	struct integrity_iint_cache *iint;
 
 	if (!IS_IMA(inode))
 		return;
 
-	write_lock(&integrity_iint_lock);
-	iint = __integrity_iint_find(inode);
-	rb_erase(&iint->rb_node, &integrity_iint_tree);
-	write_unlock(&integrity_iint_lock);
+	write_lock(&iint_tree->lock);
+	iint = __integrity_iint_rb_find(&iint_tree->root, inode);
+	if (!iint) {
+		write_unlock(&iint_tree->lock);
+		return;
+	}
+	rb_erase(&iint->rb_node, &iint_tree->root);
+	write_unlock(&iint_tree->lock);
 
 	iint_free(iint);
 }
 
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+	integrity_inode_rb_free(&init_iint_tree, inode);
+}
+
 static void init_once(void *foo)
 {
 	struct integrity_iint_cache *iint = foo;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 413c803c5208..721d1850e4f9 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -140,11 +140,20 @@ struct integrity_iint_cache {
 	struct ima_digest_data *ima_hash;
 };
 
+struct integrity_iint_tree {
+	rwlock_t lock;
+	struct rb_root root;
+};
+
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
  */
 struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
+struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree
+						    *iint_tree,
+						    const struct inode *inode);
+
 int integrity_kernel_read(struct file *file, loff_t offset,
 			  void *addr, unsigned long count);
 
@@ -155,6 +164,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
 
 extern struct dentry *integrity_dir;
 
+extern struct integrity_iint_tree init_iint_tree;
+
 struct modsig;
 
 #ifdef CONFIG_INTEGRITY_SIGNATURE
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 03/30] ima: Bind ima namespace to the file descriptor
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

IMA namespace reference will be required in ima_file_free() to check
the policy and find inode integrity data for the correct ima namespace.
ima_file_free() is called on __fput(), and __fput() may be called after
releasing namespaces in exit_task_namespaces() in do_exit() and
therefore nsproxy reference cannot be used - it is already set to NULL.

This is a preparation for namespacing policy and inode integrity data.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 fs/file_table.c                   |  6 +++++-
 include/linux/fs.h                |  3 +++
 include/linux/ima.h               |  6 ++++++
 security/integrity/ima/ima_main.c | 27 +++++++++++++++++++++++++--
 4 files changed, 39 insertions(+), 3 deletions(-)

diff --git a/fs/file_table.c b/fs/file_table.c
index 656647f9575a..878213d8af92 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -109,6 +109,8 @@ static struct file *__alloc_file(int flags, const struct cred *cred)
 		return ERR_PTR(error);
 	}
 
+	ima_file_alloc(f);
+
 	atomic_long_set(&f->f_count, 1);
 	rwlock_init(&f->f_owner.lock);
 	spin_lock_init(&f->f_lock);
@@ -259,8 +261,10 @@ static void __fput(struct file *file)
 	struct inode *inode = file->f_inode;
 	fmode_t mode = file->f_mode;
 
-	if (unlikely(!(file->f_mode & FMODE_OPENED)))
+	if (unlikely(!(file->f_mode & FMODE_OPENED))) {
+		ima_file_free(file);
 		goto out;
+	}
 
 	might_sleep();
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 407881ebeab1..8d6264755935 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -947,6 +947,9 @@ struct file {
 	struct address_space	*f_mapping;
 	errseq_t		f_wb_err;
 	errseq_t		f_sb_err; /* for syncfs */
+#ifdef CONFIG_IMA
+	void			*f_ima;
+#endif
 } __randomize_layout
   __attribute__((aligned(4)));	/* lest something weird decides that 2 is OK */
 
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 5b6235b97603..3954cef57c00 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -21,6 +21,7 @@ struct llist_node;
 extern int ima_bprm_check(struct linux_binprm *bprm);
 extern int ima_file_check(struct file *file, int mask);
 extern void ima_post_create_tmpfile(struct inode *inode);
+extern int ima_file_alloc(struct file *file);
 extern void ima_file_free(struct file *file);
 extern int ima_file_mmap(struct file *file, unsigned long prot);
 extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot);
@@ -66,6 +67,11 @@ static inline void ima_post_create_tmpfile(struct inode *inode)
 {
 }
 
+static inline int ima_file_alloc(struct file *file)
+{
+	return 0;
+}
+
 static inline void ima_file_free(struct file *file)
 {
 	return;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index d800e73c8b62..c7e29277b953 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -169,6 +169,23 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
 	mutex_unlock(&iint->mutex);
 }
 
+/**
+ * ima_file_alloc - called on __alloc_file()
+ * @file: pointer to file structure being created
+ *
+ * Bind IMA namespace to the file descriptor. This is necessary, because
+ * __fput can be called after exit_task_namespaces() in do_exit().
+ * In that case nsproxy is already NULL and ima ns has to be found
+ * differently in ima_file_free(). If process joins different ima ns, files
+ * opened in the old ns will point to that (old) ns.
+ */
+int ima_file_alloc(struct file *file)
+{
+	file->f_ima = get_current_ns();
+	get_ima_ns((struct ima_namespace *)file->f_ima);
+	return 0;
+}
+
 /**
  * ima_file_free - called on __fput()
  * @file: pointer to file structure being freed
@@ -179,15 +196,21 @@ void ima_file_free(struct file *file)
 {
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint;
+	struct ima_namespace *ima_ns = (struct ima_namespace *)file->f_ima;
+
+	if (unlikely(!(file->f_mode & FMODE_OPENED)))
+		goto out;
 
 	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
-		return;
+		goto out;
 
 	iint = integrity_iint_find(inode);
 	if (!iint)
-		return;
+		goto out;
 
 	ima_check_last_writer(iint, inode, file);
+out:
+	put_ima_ns(ima_ns);
 }
 
 static int process_measurement(struct file *file, const struct cred *cred,
-- 
2.20.1


^ permalink raw reply related

* [RFC PATCH 01/30] ima: Introduce ima namespace
From: krzysztof.struczynski @ 2020-08-18 15:20 UTC (permalink / raw)
  To: linux-integrity, linux-kernel, containers, linux-security-module
  Cc: Krzysztof Struczynski, zohar, stefanb, sunyuqiong1988, mkayaalp,
	dmitry.kasatkin, serge, jmorris, christian, silviu.vlasceanu,
	roberto.sassu
In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com>

From: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>

IMA namespace wraps global ima resources in an abstraction, to enable ima
to work with the containers. Currently, ima namespace contains no useful
data but a dummy interface. IMA resources related to different aspects of
IMA, namely IMA-audit, IMA-measurement, IMA-appraisal will be added in the
following patches.

The way how ima namespace is created is analogous to the time namespace:
unshare(CLONE_NEWIMA) system call creates a new ima namespace but doesn't
assign it to the current process. All children of the process will be born
in the new ima namespace, or a process can use setns() system call to join
the new ima namespace. Call to clone3(CLONE_NEWIMA) system call creates a
new namespace, which the new process joins instantly.

This scheme, allows to configure the new ima namespace before any process
appears in it. If user initially unshares the new ima namespace, ima can
be configured using ima entries in the securityfs. If user calls clone3()
system call directly, the new ima namespace can be configured using clone
arguments. To allow this, new securityfs entries have to be added, and
structures clone_args and kernel_clone_args have to be extended.

Early configuration is crucial. The new ima polices must apply to the
first process in the new namespace, and the appraisal key has to be loaded
beforehand.

Add a new CONFIG_IMA_NS option to the kernel configuration, that enables
one to create a new IMA namespace. IMA namespace functionality is disabled
by default.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 fs/proc/namespaces.c              |   4 +
 include/linux/ima.h               |  57 ++++++++
 include/linux/nsproxy.h           |   3 +
 include/linux/proc_ns.h           |   5 +-
 include/linux/user_namespace.h    |   1 +
 include/uapi/linux/sched.h        |   1 +
 init/Kconfig                      |  12 ++
 kernel/fork.c                     |  24 +++-
 kernel/nsproxy.c                  |  34 ++++-
 kernel/ucount.c                   |   1 +
 security/integrity/ima/Makefile   |   1 +
 security/integrity/ima/ima.h      |  13 ++
 security/integrity/ima/ima_init.c |  13 ++
 security/integrity/ima/ima_ns.c   | 232 ++++++++++++++++++++++++++++++
 14 files changed, 392 insertions(+), 9 deletions(-)
 create mode 100644 security/integrity/ima/ima_ns.c

diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 8e159fc78c0a..117812a59e5d 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -37,6 +37,10 @@ static const struct proc_ns_operations *ns_entries[] = {
 	&timens_operations,
 	&timens_for_children_operations,
 #endif
+#ifdef CONFIG_IMA_NS
+	&imans_operations,
+	&imans_for_children_operations,
+#endif
 };
 
 static const char *proc_ns_get_link(struct dentry *dentry,
diff --git a/include/linux/ima.h b/include/linux/ima.h
index d15100de6cdd..4a9c29d4d056 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -12,6 +12,9 @@
 #include <linux/kexec.h>
 struct linux_binprm;
 
+struct nsproxy;
+struct task_struct;
+
 #ifdef CONFIG_IMA
 extern int ima_bprm_check(struct linux_binprm *bprm);
 extern int ima_file_check(struct file *file, int mask);
@@ -167,4 +170,58 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
 	return false;
 }
 #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+
+struct ima_namespace {
+	struct kref kref;
+	struct ns_common ns;
+	struct ucounts *ucounts;
+	struct user_namespace *user_ns;
+} __randomize_layout;
+
+extern struct ima_namespace init_ima_ns;
+
+#ifdef CONFIG_IMA_NS
+struct ima_namespace *copy_ima_ns(unsigned long flags,
+				  struct user_namespace *user_ns,
+				  struct ima_namespace *old_ns);
+
+void free_ima_ns(struct kref *kref);
+
+int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
+
+static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
+{
+	if (ns)
+		kref_get(&ns->kref);
+	return ns;
+}
+static inline void put_ima_ns(struct ima_namespace *ns)
+{
+	if (ns)
+		kref_put(&ns->kref, free_ima_ns);
+}
+
+#else
+static inline struct ima_namespace *copy_ima_ns(unsigned long flags,
+						struct user_namespace *user_ns,
+						struct ima_namespace *old_ns)
+{
+	return old_ns;
+}
+
+static inline int imans_on_fork(struct nsproxy *nsproxy,
+				struct task_struct *tsk)
+{
+	return 0;
+}
+
+static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
+{
+	return ns;
+}
+
+static inline void put_ima_ns(struct ima_namespace *ns)
+{
+}
+#endif /* CONFIG_IMA_NS */
 #endif /* _LINUX_IMA_H */
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index cdb171efc7cb..56216a94009d 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -10,6 +10,7 @@ struct uts_namespace;
 struct ipc_namespace;
 struct pid_namespace;
 struct cgroup_namespace;
+struct ima_namespace;
 struct fs_struct;
 
 /*
@@ -38,6 +39,8 @@ struct nsproxy {
 	struct time_namespace *time_ns;
 	struct time_namespace *time_ns_for_children;
 	struct cgroup_namespace *cgroup_ns;
+	struct ima_namespace *ima_ns;
+	struct ima_namespace *ima_ns_for_children;
 };
 extern struct nsproxy init_nsproxy;
 
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index 75807ecef880..93735b7bbb65 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -16,7 +16,7 @@ struct inode;
 struct proc_ns_operations {
 	const char *name;
 	const char *real_ns_name;
-	int type;
+	unsigned long type;
 	struct ns_common *(*get)(struct task_struct *task);
 	void (*put)(struct ns_common *ns);
 	int (*install)(struct nsset *nsset, struct ns_common *ns);
@@ -34,6 +34,8 @@ extern const struct proc_ns_operations mntns_operations;
 extern const struct proc_ns_operations cgroupns_operations;
 extern const struct proc_ns_operations timens_operations;
 extern const struct proc_ns_operations timens_for_children_operations;
+extern const struct proc_ns_operations imans_operations;
+extern const struct proc_ns_operations imans_for_children_operations;
 
 /*
  * We always define these enumerators
@@ -46,6 +48,7 @@ enum {
 	PROC_PID_INIT_INO	= 0xEFFFFFFCU,
 	PROC_CGROUP_INIT_INO	= 0xEFFFFFFBU,
 	PROC_TIME_INIT_INO	= 0xEFFFFFFAU,
+	PROC_IMA_INIT_INO	= 0xEFFFFFF9U,
 };
 
 #ifdef CONFIG_PROC_FS
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 6ef1c7109fc4..d9759c54fead 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -46,6 +46,7 @@ enum ucount_type {
 	UCOUNT_MNT_NAMESPACES,
 	UCOUNT_CGROUP_NAMESPACES,
 	UCOUNT_TIME_NAMESPACES,
+	UCOUNT_IMA_NAMESPACES,
 #ifdef CONFIG_INOTIFY_USER
 	UCOUNT_INOTIFY_INSTANCES,
 	UCOUNT_INOTIFY_WATCHES,
diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h
index 3bac0a8ceab2..b30e27efee92 100644
--- a/include/uapi/linux/sched.h
+++ b/include/uapi/linux/sched.h
@@ -36,6 +36,7 @@
 /* Flags for the clone3() syscall. */
 #define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */
 #define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given the right permissions. */
+#define CLONE_NEWIMA 0x400000000ULL /* New IMA namespace. */
 
 /*
  * cloning flags intersect with CSIGNAL so can be used with unshare and clone3
diff --git a/init/Kconfig b/init/Kconfig
index d6a0b31b13dc..f188b33588a2 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1184,6 +1184,18 @@ 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 IMA
+	default n
+	help
+	  This allows container engines to use ima namespaces to provide
+	  different IMA policy rules for different containers. Also, it allows
+	  to create what appear to be multiple instances of the IMA measurement
+	  list and other IMA related resources.
+
+	  If unsure, say N.
+
 endif # NAMESPACES
 
 config CHECKPOINT_RESTORE
diff --git a/kernel/fork.c b/kernel/fork.c
index 35e9894d394c..b977fd92fe3f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1876,11 +1876,24 @@ static __latent_entropy struct task_struct *copy_process(
 	}
 
 	/*
-	 * If the new process will be in a different time namespace
-	 * do not allow it to share VM or a thread group with the forking task.
+	 * If the new process will be in a different time namespace or a
+	 * different ima namespace, do not allow it to share VM or a thread
+	 * group with the forking task.
 	 */
 	if (clone_flags & (CLONE_THREAD | CLONE_VM)) {
-		if (nsp->time_ns != nsp->time_ns_for_children)
+		if ((nsp->time_ns != nsp->time_ns_for_children) ||
+		    ((clone_flags & CLONE_NEWIMA) ||
+		     (nsp->ima_ns != nsp->ima_ns_for_children)))
+			return ERR_PTR(-EINVAL);
+	}
+
+	/*
+	 * If the new process will be in a different ima namespace
+	 * do not allow it to share the same file descriptor table.
+	 */
+	if (clone_flags & CLONE_FILES) {
+		if ((clone_flags & CLONE_NEWIMA) ||
+		    (nsp->ima_ns != nsp->ima_ns_for_children))
 			return ERR_PTR(-EINVAL);
 	}
 
@@ -2649,7 +2662,8 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs)
 {
 	/* Verify that no unknown flags are passed along. */
 	if (kargs->flags &
-	    ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP))
+	    ~(CLONE_LEGACY_FLAGS |
+	      CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP | CLONE_NEWIMA))
 		return false;
 
 	/*
@@ -2796,7 +2810,7 @@ static int check_unshare_flags(unsigned long unshare_flags)
 				CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
 				CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|
 				CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP|
-				CLONE_NEWTIME))
+				CLONE_NEWTIME|CLONE_NEWIMA))
 		return -EINVAL;
 	/*
 	 * Not implemented, but pretend it works if there is nothing
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 12dd41b39a7f..791efffd7a03 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -19,6 +19,7 @@
 #include <net/net_namespace.h>
 #include <linux/ipc_namespace.h>
 #include <linux/time_namespace.h>
+#include <linux/ima.h>
 #include <linux/fs_struct.h>
 #include <linux/proc_fs.h>
 #include <linux/proc_ns.h>
@@ -47,6 +48,10 @@ struct nsproxy init_nsproxy = {
 	.time_ns		= &init_time_ns,
 	.time_ns_for_children	= &init_time_ns,
 #endif
+#ifdef CONFIG_IMA_NS
+	.ima_ns			= &init_ima_ns,
+	.ima_ns_for_children	= &init_ima_ns,
+#endif
 };
 
 static inline struct nsproxy *create_nsproxy(void)
@@ -121,8 +126,19 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
 	}
 	new_nsp->time_ns = get_time_ns(tsk->nsproxy->time_ns);
 
+	new_nsp->ima_ns_for_children = copy_ima_ns(flags, user_ns,
+					tsk->nsproxy->ima_ns_for_children);
+	if (IS_ERR(new_nsp->ima_ns_for_children)) {
+		err = PTR_ERR(new_nsp->ima_ns_for_children);
+		goto out_ima;
+	}
+	new_nsp->ima_ns = get_ima_ns(tsk->nsproxy->ima_ns);
+
 	return new_nsp;
 
+out_ima:
+	put_time_ns(new_nsp->time_ns);
+	put_time_ns(new_nsp->time_ns_for_children);
 out_time:
 	put_net(new_nsp->net_ns);
 out_net:
@@ -157,8 +173,10 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
 
 	if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
 			      CLONE_NEWPID | CLONE_NEWNET |
-			      CLONE_NEWCGROUP | CLONE_NEWTIME)))) {
-		if (likely(old_ns->time_ns_for_children == old_ns->time_ns)) {
+			      CLONE_NEWCGROUP | CLONE_NEWTIME |
+			      CLONE_NEWIMA)))) {
+		if (likely((old_ns->time_ns_for_children == old_ns->time_ns) &&
+			   (old_ns->ima_ns_for_children == old_ns->ima_ns))) {
 			get_nsproxy(old_ns);
 			return 0;
 		}
@@ -186,6 +204,12 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
 		return ret;
 	}
 
+	ret = imans_on_fork(new_ns, tsk);
+	if (ret) {
+		free_nsproxy(new_ns);
+		return ret;
+	}
+
 	tsk->nsproxy = new_ns;
 	return 0;
 }
@@ -204,6 +228,10 @@ void free_nsproxy(struct nsproxy *ns)
 		put_time_ns(ns->time_ns);
 	if (ns->time_ns_for_children)
 		put_time_ns(ns->time_ns_for_children);
+	if (ns->ima_ns)
+		put_ima_ns(ns->ima_ns);
+	if (ns->ima_ns_for_children)
+		put_ima_ns(ns->ima_ns_for_children);
 	put_cgroup_ns(ns->cgroup_ns);
 	put_net(ns->net_ns);
 	kmem_cache_free(nsproxy_cachep, ns);
@@ -221,7 +249,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
 
 	if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
 			       CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP |
-			       CLONE_NEWTIME)))
+			       CLONE_NEWTIME | CLONE_NEWIMA)))
 		return 0;
 
 	user_ns = new_cred ? new_cred->user_ns : current_user_ns();
diff --git a/kernel/ucount.c b/kernel/ucount.c
index 11b1596e2542..3f4768d62b8f 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -70,6 +70,7 @@ static struct ctl_table user_table[] = {
 	UCOUNT_ENTRY("max_mnt_namespaces"),
 	UCOUNT_ENTRY("max_cgroup_namespaces"),
 	UCOUNT_ENTRY("max_time_namespaces"),
+	UCOUNT_ENTRY("max_ima_namespaces"),
 #ifdef CONFIG_INOTIFY_USER
 	UCOUNT_ENTRY("max_inotify_instances"),
 	UCOUNT_ENTRY("max_inotify_watches"),
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 67dabca670e2..d804d93f1a99 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -14,3 +14,4 @@ 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
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 38043074ce5e..603da5b2db08 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -20,6 +20,7 @@
 #include <linux/hash.h>
 #include <linux/tpm.h>
 #include <linux/audit.h>
+#include <linux/ima.h>
 #include <crypto/hash_info.h>
 
 #include "../integrity.h"
@@ -371,6 +372,18 @@ static inline int ima_read_xattr(struct dentry *dentry,
 
 #endif /* CONFIG_IMA_APPRAISE */
 
+#ifdef CONFIG_IMA_NS
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return current->nsproxy->ima_ns;
+}
+#else
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return &init_ima_ns;
+}
+#endif /* CONFIG_IMA_NS */
+
 #ifdef CONFIG_IMA_APPRAISE_MODSIG
 int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
 		    struct modsig **modsig);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 4902fe7bd570..013bbec16849 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -15,6 +15,9 @@
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/err.h>
+#include <linux/kref.h>
+#include <linux/proc_ns.h>
+#include <linux/user_namespace.h>
 
 #include "ima.h"
 
@@ -22,6 +25,16 @@
 const char boot_aggregate_name[] = "boot_aggregate";
 struct tpm_chip *ima_tpm_chip;
 
+struct ima_namespace init_ima_ns = {
+	.kref = KREF_INIT(2),
+	.user_ns = &init_user_ns,
+	.ns.inum = PROC_IMA_INIT_INO,
+#ifdef CONFIG_IMA_NS
+	.ns.ops = &imans_operations,
+#endif
+};
+EXPORT_SYMBOL(init_ima_ns);
+
 /* Add the boot aggregate to the IMA measurement list and extend
  * the PCR register.
  *
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..8f5f301406a2
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2020 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_ns.c
+ *      Functions to manage the IMA namespace.
+ */
+
+#include <linux/export.h>
+#include <linux/ima.h>
+#include <linux/kref.h>
+#include <linux/proc_ns.h>
+#include <linux/slab.h>
+#include <linux/user_namespace.h>
+#include <linux/nsproxy.h>
+#include <linux/sched.h>
+
+#include "ima.h"
+
+static struct ucounts *inc_ima_namespaces(struct user_namespace *ns)
+{
+	return inc_ucount(ns, current_euid(), UCOUNT_IMA_NAMESPACES);
+}
+
+static void dec_ima_namespaces(struct ucounts *ucounts)
+{
+	return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
+}
+
+static struct ima_namespace *ima_ns_alloc(void)
+{
+	struct ima_namespace *ima_ns;
+
+	ima_ns = kzalloc(sizeof(*ima_ns), GFP_KERNEL);
+	if (!ima_ns)
+		return NULL;
+
+	return ima_ns;
+}
+
+/**
+ * Clone a new ns copying an original ima namespace, setting refcount to 1
+ *
+ * @user_ns:	User namespace that current task runs in
+ * @old_ns:	Old ima namespace to clone
+ * Return:	ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise
+ */
+static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
+					   struct ima_namespace *old_ns)
+{
+	struct ima_namespace *ns;
+	struct ucounts *ucounts;
+	int err;
+
+	err = -ENOSPC;
+	ucounts = inc_ima_namespaces(user_ns);
+	if (!ucounts)
+		goto fail;
+
+	err = -ENOMEM;
+	ns = ima_ns_alloc();
+	if (!ns)
+		goto fail_dec;
+
+	kref_init(&ns->kref);
+
+	err = ns_alloc_inum(&ns->ns);
+	if (err)
+		goto fail_free;
+
+	ns->ns.ops = &imans_operations;
+	ns->user_ns = get_user_ns(user_ns);
+	ns->ucounts = ucounts;
+
+	return ns;
+
+fail_free:
+	kfree(ns);
+fail_dec:
+	dec_ima_namespaces(ucounts);
+fail:
+	return ERR_PTR(err);
+}
+
+/**
+ * Copy task's ima namespace, or clone it if flags specifies CLONE_NEWNS.
+ *
+ * @flags:      Cloning flags
+ * @user_ns:	User namespace that current task runs in
+ * @old_ns:	Old ima namespace to clone
+ *
+ * Return: IMA namespace or ERR_PTR.
+ */
+
+struct ima_namespace *copy_ima_ns(unsigned long flags,
+				  struct user_namespace *user_ns,
+				  struct ima_namespace *old_ns)
+{
+	if (!(flags & CLONE_NEWIMA))
+		return get_ima_ns(old_ns);
+
+	return clone_ima_ns(user_ns, old_ns);
+}
+
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+	dec_ima_namespaces(ns->ucounts);
+	put_user_ns(ns->user_ns);
+	ns_free_inum(&ns->ns);
+	kfree(ns);
+}
+
+void free_ima_ns(struct kref *kref)
+{
+	struct ima_namespace *ns;
+
+	ns = container_of(kref, struct ima_namespace, kref);
+
+	destroy_ima_ns(ns);
+}
+
+static inline struct ima_namespace *to_ima_ns(struct ns_common *ns)
+{
+	return container_of(ns, struct ima_namespace, ns);
+}
+
+static struct ns_common *imans_get(struct task_struct *task)
+{
+	struct ima_namespace *ns = NULL;
+	struct nsproxy *nsproxy;
+
+	task_lock(task);
+	nsproxy = task->nsproxy;
+	if (nsproxy) {
+		ns = nsproxy->ima_ns;
+		get_ima_ns(ns);
+	}
+	task_unlock(task);
+
+	return ns ? &ns->ns : NULL;
+}
+
+static struct ns_common *imans_for_children_get(struct task_struct *task)
+{
+	struct ima_namespace *ns = NULL;
+	struct nsproxy *nsproxy;
+
+	task_lock(task);
+	nsproxy = task->nsproxy;
+	if (nsproxy) {
+		ns = nsproxy->ima_ns_for_children;
+		get_ima_ns(ns);
+	}
+	task_unlock(task);
+
+	return ns ? &ns->ns : NULL;
+}
+
+static void imans_put(struct ns_common *ns)
+{
+	put_ima_ns(to_ima_ns(ns));
+}
+
+static int imans_install(struct nsset *nsset, struct ns_common *new)
+{
+	struct nsproxy *nsproxy = nsset->nsproxy;
+	struct ima_namespace *ns = to_ima_ns(new);
+
+	if (!current_is_single_threaded())
+		return -EUSERS;
+
+	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
+	    !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
+		return -EPERM;
+
+	get_ima_ns(ns);
+	put_ima_ns(nsproxy->ima_ns);
+	nsproxy->ima_ns = ns;
+
+	get_ima_ns(ns);
+	put_ima_ns(nsproxy->ima_ns_for_children);
+	nsproxy->ima_ns_for_children = ns;
+
+	return 0;
+}
+
+int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
+{
+	struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns;
+	struct ima_namespace *ns = to_ima_ns(nsc);
+
+	/* create_new_namespaces() already incremented the ref counter */
+	if (nsproxy->ima_ns == nsproxy->ima_ns_for_children)
+		return 0;
+
+	get_ima_ns(ns);
+	put_ima_ns(nsproxy->ima_ns);
+	nsproxy->ima_ns = ns;
+
+	return 0;
+}
+
+static struct user_namespace *imans_owner(struct ns_common *ns)
+{
+	return to_ima_ns(ns)->user_ns;
+}
+
+const struct proc_ns_operations imans_operations = {
+	.name = "ima",
+	.type = CLONE_NEWIMA,
+	.get = imans_get,
+	.put = imans_put,
+	.install = imans_install,
+	.owner = imans_owner,
+};
+
+const struct proc_ns_operations imans_for_children_operations = {
+	.name = "ima_for_children",
+	.type = CLONE_NEWIMA,
+	.get = imans_for_children_get,
+	.put = imans_put,
+	.install = imans_install,
+	.owner = imans_owner,
+};
+
-- 
2.20.1


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox