linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures
@ 2020-08-18 15:42 krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 11/30] ima: Keep track of the measurment list per ima namespace krzysztof.struczynski
                   ` (19 more replies)
  0 siblings, 20 replies; 21+ messages in thread
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	[flat|nested] 21+ messages in thread

* [RFC PATCH 11/30] ima: Keep track of the measurment list per ima namespace
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 12/30] ima: Check ima namespace ID during digest entry lookup krzysztof.struczynski
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 12/30] ima: Check ima namespace ID during digest entry lookup
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 11/30] ima: Keep track of the measurment list per ima namespace krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 13/30] ima: Add a new ima template that includes namespace ID krzysztof.struczynski
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 13/30] ima: Add a new ima template that includes namespace ID
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 11/30] ima: Keep track of the measurment list per ima namespace krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 12/30] ima: Check ima namespace ID during digest entry lookup krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 14/30] ima: Add per namespace view of the measurement list krzysztof.struczynski
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 14/30] ima: Add per namespace view of the measurement list
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (2 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 13/30] ima: Add a new ima template that includes namespace ID krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 15/30] ima: Add a reader counter to the integrity inode data krzysztof.struczynski
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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>

Modify ima securityfs interface, so that only measurement list entries
that belong to the given ima namespace are visible/counted. The initial
ima namespace is an exception, its processes have access to all
measurement list entries.

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

diff --git a/include/linux/ima.h b/include/linux/ima.h
index df22143ffe30..d59ed38a4305 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -203,6 +203,7 @@ struct ima_namespace {
 	struct ima_policy_data *policy_data;
 	struct integrity_iint_tree *iint_tree;
 	struct list_head *measurements;
+	atomic_long_t ml_len; /* number of stored measurements in the list */
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 4758e14c4a7b..e2893f0b0f31 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -63,7 +63,9 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
 {
-	return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	return ima_show_htable_value(buf, count, ppos, &ima_ns->ml_len);
 
 }
 
@@ -77,10 +79,36 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 {
 	loff_t l = *pos;
 	struct ima_queue_entry *qe;
+	struct ima_namespace *ima_ns = get_current_ns();
+	unsigned int ns_id = get_ns_id(ima_ns);
+
+	if (ima_ns == &init_ima_ns) {
+		/* we need a lock since pos could point beyond last element */
+		rcu_read_lock();
+		list_for_each_entry_rcu(qe, &ima_measurements, later) {
+			if (!l--) {
+				rcu_read_unlock();
+				return qe;
+			}
+		}
+		rcu_read_unlock();
+		return NULL;
+	}
 
-	/* we need a lock since pos could point beyond last element */
 	rcu_read_lock();
-	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+	qe = list_next_or_null_rcu(&ima_measurements,
+				   ima_ns->measurements,
+				   struct ima_queue_entry,
+				   later);
+	if (!qe) {
+		rcu_read_unlock();
+		return NULL;
+	}
+
+	list_for_each_entry_from_rcu(qe, &ima_measurements, later) {
+		if (ns_id != qe->entry->ns_id)
+			continue;
+
 		if (!l--) {
 			rcu_read_unlock();
 			return qe;
@@ -93,12 +121,27 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 {
 	struct ima_queue_entry *qe = v;
+	struct ima_namespace *ima_ns = get_current_ns();
+	unsigned int ns_id = get_ns_id(ima_ns);
+
+	if (ima_ns == &init_ima_ns) {
+		/* lock protects when reading beyond last element
+		 * against concurrent list-extension
+		 */
+		rcu_read_lock();
+		qe = list_entry_rcu(qe->later.next, struct ima_queue_entry,
+				    later);
+		rcu_read_unlock();
+		(*pos)++;
+
+		return (&qe->later == &ima_measurements) ? NULL : qe;
+	}
 
-	/* lock protects when reading beyond last element
-	 * against concurrent list-extension
-	 */
 	rcu_read_lock();
-	qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later);
+	list_for_each_entry_continue_rcu(qe, &ima_measurements, later) {
+		if (ns_id == qe->entry->ns_id)
+			break;
+	}
 	rcu_read_unlock();
 	(*pos)++;
 
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 2100ee341dfc..ac9509d8c0f0 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -36,6 +36,7 @@ struct ima_namespace init_ima_ns = {
 	.policy_data = &init_policy_data,
 	.iint_tree = &init_iint_tree,
 	.measurements = &ima_measurements,
+	.ml_len = ATOMIC_LONG_INIT(0),
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index f331187a4d3c..81de492baa99 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -126,6 +126,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	ns->user_ns = get_user_ns(user_ns);
 	ns->ucounts = ucounts;
 	ns->frozen = false;
+	atomic_long_set(&ns->ml_len, 0);
 
 	rwlock_init(&ns->iint_tree->lock);
 	ns->iint_tree->root = RB_ROOT;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index bd890778c5be..ec5b3ca3ef92 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -227,6 +227,14 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 		goto out;
 	}
 
+	/* Initial ima namespace has access to all measurement list entries,
+	 * therefore always increment its measurement list length. Other
+	 * namespaces can see only their own entries.
+	 */
+	if (ima_ns != &init_ima_ns)
+		atomic_long_inc(&ima_ns->ml_len);
+	atomic_long_inc(&init_ima_ns.ml_len);
+
 	if (violation)		/* invalidate pcr */
 		digests_arg = digests;
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 15/30] ima: Add a reader counter to the integrity inode data
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (3 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 14/30] ima: Add per namespace view of the measurement list krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 16/30] ima: Extend permissions to the ima securityfs entries krzysztof.struczynski
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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>

To detect ToMToU violations reader counter of the given inode is
checked. This is not enough, because the reader may exist in a
different ima namespace. Per inode reader counter tracks readers in all
ima namespaces, whereas the per namespace counter is necessary to avoid
false positives.

Add a new reader counter to the integrity inode cache entry.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima_main.c | 71 ++++++++++++++++++++++---------
 security/integrity/integrity.h    | 18 ++++++++
 2 files changed, 70 insertions(+), 19 deletions(-)

diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index b933c7e6c8e1..6555cdf6b65e 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -28,6 +28,11 @@
 
 #include "ima.h"
 
+struct ima_file_data {
+	struct ima_namespace *ima_ns;
+	bool is_readcount;
+};
+
 int ima_hash_algo = HASH_ALGO_SHA1;
 static int hash_setup_done;
 
@@ -116,8 +121,8 @@ static void ima_rdwr_violation_check(struct file *file,
 				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))
+			if (iint && atomic_read(&iint->readcount) &&
+			    test_bit(IMA_MUST_MEASURE, &iint->atomic_flags))
 				send_tomtou = true;
 		}
 	} else {
@@ -171,7 +176,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
 {
 	fmode_t mode = file->f_mode;
 	bool update;
-	struct ima_namespace *ima_ns = (struct ima_namespace *)file->f_ima;
+	struct ima_file_data *f_data = (struct ima_file_data *)file->f_ima;
 
 	if (!(mode & FMODE_WRITE))
 		return;
@@ -186,7 +191,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
 			iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
 			iint->measured_pcrs = 0;
 
-			ima_check_active_ns(ima_ns, inode);
+			ima_check_active_ns(f_data->ima_ns, inode);
 
 			if (update)
 				ima_update_xattr(iint, file);
@@ -207,8 +212,18 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
  */
 int ima_file_alloc(struct file *file)
 {
-	file->f_ima = get_current_ns();
-	get_ima_ns((struct ima_namespace *)file->f_ima);
+	struct ima_file_data *f_data;
+
+	f_data = kmalloc(sizeof(struct ima_file_data), GFP_KERNEL);
+	if (!f_data)
+		return -ENOMEM;
+
+	f_data->ima_ns = get_current_ns();
+	f_data->is_readcount = false;
+	get_ima_ns(f_data->ima_ns);
+
+	file->f_ima = f_data;
+
 	return 0;
 }
 
@@ -222,27 +237,33 @@ 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;
+	struct ima_file_data *f_data = (struct ima_file_data *)file->f_ima;
 
 	if (unlikely(!(file->f_mode & FMODE_OPENED)))
 		goto out;
 
-	if (!ima_ns->policy_data->ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!f_data->ima_ns->policy_data->ima_policy_flag ||
+	    !S_ISREG(inode->i_mode))
 		goto out;
 
-	iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
+	iint = integrity_iint_rb_find(f_data->ima_ns->iint_tree, inode);
 	if (!iint)
 		goto out;
 
 	ima_check_last_writer(iint, inode, file);
+
+	if (f_data->is_readcount)
+		iint_readcount_dec(iint);
 out:
-	put_ima_ns(ima_ns);
+	put_ima_ns(f_data->ima_ns);
+	kfree(f_data);
 }
 
 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 ima_namespace *ima_ns,
+				  bool readcount_open)
 {
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
@@ -262,6 +283,12 @@ static int process_ns_measurement(struct file *file, const struct cred *cred,
 	if (!ima_ns->policy_data->ima_policy_flag)
 		return 0;
 
+	if (ima_ns != current_ima_ns) {
+		iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
+		if (!iint)
+			return 0;
+	}
+
 	/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
 	 * bitmask based on the appraise/audit/measurement policy.
 	 * Included is the appraise submask.
@@ -282,12 +309,18 @@ static int process_ns_measurement(struct file *file, const struct cred *cred,
 
 	inode_lock(inode);
 
-	if (action) {
+	if (action && !iint) {
 		iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
 		if (!iint)
 			rc = -ENOMEM;
 	}
 
+	if ((ima_ns == current_ima_ns) && iint && readcount_open &&
+	    ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)) {
+		iint_readcount_inc(iint);
+		((struct ima_file_data *)file->f_ima)->is_readcount = true;
+	}
+
 	if (!rc && violation_check)
 		ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
 					 &pathbuf, &pathname, filename, ima_ns);
@@ -429,7 +462,7 @@ static int process_ns_measurement(struct file *file, const struct cred *cred,
 
 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, bool readcount_open)
 {
 	int ret;
 	struct ima_namespace *ima_ns;
@@ -444,7 +477,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 			continue;
 
 		ret = process_ns_measurement(file, cred, secid, buf, size, mask,
-					     func, ima_ns);
+					     func, ima_ns, readcount_open);
 		if (ret != 0)
 			break;
 	}
@@ -471,7 +504,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, true);
 	}
 
 	return 0;
@@ -551,13 +584,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, false);
 	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, false);
 }
 
 /**
@@ -577,7 +610,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, true);
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
@@ -779,7 +812,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, false);
 }
 
 /**
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 721d1850e4f9..c2981a98547e 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -17,6 +17,7 @@
 #include <crypto/sha.h>
 #include <linux/key.h>
 #include <linux/audit.h>
+#include <linux/atomic.h>
 
 /* iint action cache flags */
 #define IMA_MEASURE		0x00000001
@@ -138,6 +139,9 @@ struct integrity_iint_cache {
 	enum integrity_status ima_creds_status:4;
 	enum integrity_status evm_status:4;
 	struct ima_digest_data *ima_hash;
+	atomic_t readcount; /* Reader counter, incremented only in FILE_CHECK or
+			     * MMAP_CHECK hooks, used for ima violation check.
+			     */
 };
 
 struct integrity_iint_tree {
@@ -294,3 +298,17 @@ static inline void __init add_to_platform_keyring(const char *source,
 {
 }
 #endif
+
+#ifdef CONFIG_IMA
+static inline void iint_readcount_inc(struct integrity_iint_cache *iint)
+{
+	atomic_inc(&iint->readcount);
+}
+static inline void iint_readcount_dec(struct integrity_iint_cache *iint)
+{
+	if (WARN_ON(!atomic_read(&iint->readcount)))
+		return;
+
+	atomic_dec(&iint->readcount);
+}
+#endif
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 16/30] ima: Extend permissions to the ima securityfs entries
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (4 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 15/30] ima: Add a reader counter to the integrity inode data krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 17/30] ima: Add the violation counter to the namespace krzysztof.struczynski
                   ` (13 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 "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	[flat|nested] 21+ messages in thread

* [RFC PATCH 17/30] ima: Add the violation counter to the namespace
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (5 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 16/30] ima: Extend permissions to the ima securityfs entries krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 18/30] ima: Change the owning user namespace of the ima namespace if necessary krzysztof.struczynski
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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>

The violations are now tracked per namespace.

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

diff --git a/include/linux/ima.h b/include/linux/ima.h
index d59ed38a4305..7eb7a008c5fe 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -204,6 +204,7 @@ struct ima_namespace {
 	struct integrity_iint_tree *iint_tree;
 	struct list_head *measurements;
 	atomic_long_t ml_len; /* number of stored measurements in the list */
+	atomic_long_t violations;
 } __randomize_layout;
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7318fff3ccaa..ef95522cc710 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -193,7 +193,6 @@ extern spinlock_t ima_queue_lock;
 
 struct ima_h_table {
 	atomic_long_t len;	/* number of stored measurements in the list */
-	atomic_long_t violations;
 	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
 };
 extern struct ima_h_table ima_htable;
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index b01451b34a98..f91516885033 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -151,7 +151,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 	event_data.ns_id = get_ns_id(ima_ns);
 
 	/* can overflow, only indicator */
-	atomic_long_inc(&ima_htable.violations);
+	atomic_long_inc(&ima_ns->violations);
 
 	result = ima_alloc_init_template(&event_data, &entry, NULL);
 	if (result < 0) {
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 6d370874d80f..b71c2552ac15 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -51,10 +51,23 @@ static ssize_t ima_show_htable_violations(struct file *filp,
 					  char __user *buf,
 					  size_t count, loff_t *ppos)
 {
-	return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+	struct ima_namespace *ima_ns = get_current_ns();
+
+	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,
 	.read = ima_show_htable_violations,
 	.llseek = generic_file_llseek,
 };
@@ -76,7 +89,6 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 	struct ima_namespace *ima_ns = get_current_ns();
 
 	return ima_show_htable_value(buf, count, ppos, &ima_ns->ml_len);
-
 }
 
 static const struct file_operations ima_measurements_count_ops = {
@@ -552,7 +564,7 @@ int __init ima_fs_init(void)
 		goto out;
 
 	violations =
-	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+	    securityfs_create_file("violations", S_IRUSR | S_IRGRP | S_IROTH,
 				   ima_dir, NULL, &ima_htable_violations_ops);
 	if (IS_ERR(violations))
 		goto out;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index ac9509d8c0f0..f7b25b3f95de 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -37,6 +37,7 @@ struct ima_namespace init_ima_ns = {
 	.iint_tree = &init_iint_tree,
 	.measurements = &ima_measurements,
 	.ml_len = ATOMIC_LONG_INIT(0),
+	.violations = ATOMIC_LONG_INIT(0),
 };
 EXPORT_SYMBOL(init_ima_ns);
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 81de492baa99..3012287b22d2 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -127,6 +127,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
 	ns->ucounts = ucounts;
 	ns->frozen = false;
 	atomic_long_set(&ns->ml_len, 0);
+	atomic_long_set(&ns->violations, 0);
 
 	rwlock_init(&ns->iint_tree->lock);
 	ns->iint_tree->root = RB_ROOT;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index ec5b3ca3ef92..912e6097af5b 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -34,7 +34,6 @@ static unsigned long binary_runtime_size = ULONG_MAX;
 /* key: inode (before secure-hashing a file) */
 struct ima_h_table ima_htable = {
 	.len = ATOMIC_LONG_INIT(0),
-	.violations = ATOMIC_LONG_INIT(0),
 	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
 };
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 18/30] ima: Change the owning user namespace of the ima namespace if necessary
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (6 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 17/30] ima: Add the violation counter to the namespace krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 19/30] ima: Configure the new ima namespace from securityfs krzysztof.struczynski
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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>

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	[flat|nested] 21+ messages in thread

* [RFC PATCH 19/30] ima: Configure the new ima namespace from securityfs
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (7 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 18/30] ima: Change the owning user namespace of the ima namespace if necessary krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 20/30] ima: Parse per ima namespace policy file krzysztof.struczynski
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 20/30] ima: Parse per ima namespace policy file
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (8 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 19/30] ima: Configure the new ima namespace from securityfs krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 21/30] user namespace: Add function that checks if the UID map is defined krzysztof.struczynski
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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>

Parse per ima namespace policy file if the file path is set.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 security/integrity/ima/ima.h        |  7 ++++---
 security/integrity/ima/ima_fs.c     | 17 +++++++++++------
 security/integrity/ima/ima_ns.c     | 18 ++++++++++++++++--
 security/integrity/ima/ima_policy.c | 25 +++++++++++--------------
 4 files changed, 42 insertions(+), 25 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 48d09efaffbe..b55d25c2bf63 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -309,10 +309,10 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
 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(struct ima_namespace *ima_ns);
 void ima_update_policy_flag(struct ima_namespace *ima_ns);
-ssize_t ima_parse_add_rule(char *);
-void ima_delete_rules(void);
+ssize_t ima_parse_add_rule(char *rule, struct ima_namespace *ima_ns);
+void ima_delete_rules(struct ima_namespace *ima_ns);
 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);
@@ -439,6 +439,7 @@ 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_read_ns_policy(char *path, struct ima_namespace *ima_ns);
 
 ssize_t ima_ns_write_policy_for_children(struct ima_namespace *ima_ns,
 					 char *policy_path);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 6c033857f521..7b93c338a478 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -343,7 +343,7 @@ static const struct file_operations ima_ascii_measurements_ops = {
 	.release = seq_release,
 };
 
-static ssize_t ima_read_policy(char *path)
+static ssize_t ima_read_policy(char *path, struct ima_namespace *ima_ns)
 {
 	void *data;
 	char *datap;
@@ -365,7 +365,7 @@ static ssize_t ima_read_policy(char *path)
 	datap = data;
 	while (size > 0 && (p = strsep(&datap, "\n"))) {
 		pr_debug("rule: %s\n", p);
-		rc = ima_parse_add_rule(p);
+		rc = ima_parse_add_rule(p, ima_ns);
 		if (rc < 0)
 			break;
 		size -= rc;
@@ -406,7 +406,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 		goto out_free;
 
 	if (data[0] == '/') {
-		result = ima_read_policy(data);
+		result = ima_read_policy(data, ima_ns);
 	} 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,
@@ -414,7 +414,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 				    1, 0);
 		result = -EACCES;
 	} else {
-		result = ima_parse_add_rule(data);
+		result = ima_parse_add_rule(data, ima_ns);
 	}
 	mutex_unlock(&ima_write_mutex);
 out_free:
@@ -502,13 +502,13 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 			    "policy_update", cause, !valid_policy, 0);
 
 	if (!valid_policy) {
-		ima_delete_rules();
+		ima_delete_rules(ima_ns);
 		valid_policy = 1;
 		clear_bit(IMA_FS_BUSY, &ima_fs_flags);
 		return 0;
 	}
 
-	ima_update_policy();
+	ima_update_policy(ima_ns);
 #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
 	securityfs_remove(ima_policy);
 	ima_policy = NULL;
@@ -629,6 +629,11 @@ static const struct file_operations ima_kcmd_for_children_ops = {
 	.open = ima_open_for_children,
 	.write = ima_write_kcmd_for_children,
 };
+
+ssize_t ima_read_ns_policy(char *path, struct ima_namespace *ima_ns)
+{
+	return ima_read_policy(path, ima_ns);
+}
 #endif /* CONFIG_IMA_NS */
 
 int __init ima_fs_init(void)
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index dd0328ee715f..ec3abc803c82 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -91,10 +91,24 @@ static int ima_set_ns_policy(struct ima_namespace *ima_ns)
 		setup_data.ima_appraise = IMA_APPRAISE_ENFORCE;
 #endif
 		ima_init_ns_policy(ima_ns, &setup_data);
-		return result;
+	} else {
+		ima_init_ns_policy(ima_ns, ima_ns->policy_setup_for_children);
 	}
 
-	ima_init_ns_policy(ima_ns, ima_ns->policy_setup_for_children);
+	if (ima_ns->policy_path_for_children) {
+		result = ima_read_ns_policy(ima_ns->policy_path_for_children,
+					    ima_ns);
+		if ((result >= 0) && !ima_check_policy(ima_ns)) {
+			integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+					    "policy_update", "completed", 0, 0);
+			ima_update_policy(ima_ns);
+		} else {
+			ima_delete_rules(ima_ns);
+			ima_delete_ns_rules(ima_ns->policy_data, false);
+			integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+					    "policy_update", "failed", 1, 0);
+		}
+	}
 
 	return result;
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 3d712c062ed0..d4774eab6a98 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -735,7 +735,8 @@ static void add_rules(struct ima_namespace *ima_ns,
 	}
 }
 
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry);
+static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
+			  struct ima_namespace *ima_ns);
 
 static int __init ima_init_arch_policy(void)
 {
@@ -765,7 +766,8 @@ static int __init ima_init_arch_policy(void)
 		result = strlcpy(rule, *rules, sizeof(rule));
 
 		INIT_LIST_HEAD(&arch_policy_entry[i].list);
-		result = ima_parse_rule(rule, &arch_policy_entry[i]);
+		result = ima_parse_rule(rule, &arch_policy_entry[i],
+					&init_ima_ns);
 		if (result) {
 			pr_warn("Skipping unknown architecture policy rule: %s\n",
 				rule);
@@ -895,10 +897,8 @@ int ima_check_policy(const struct ima_namespace *ima_ns)
  * Policy rules are never deleted so ima_policy_flag gets zeroed only once when
  * we switch from the default policy to user defined.
  */
-void ima_update_policy(void)
+void ima_update_policy(struct ima_namespace *ima_ns)
 {
-	/* 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_ns->policy_data->ima_temp_rules,
@@ -1151,7 +1151,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
 	return true;
 }
 
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
+static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
+			  struct ima_namespace *ima_ns)
 {
 	struct audit_buffer *ab;
 	char *from;
@@ -1160,7 +1161,6 @@ 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);
@@ -1547,19 +1547,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 /**
  * ima_parse_add_rule - add a rule to ima_policy_rules
  * @rule - ima measurement policy rule
+ * @ima_ns - pointer to the ima namespace the rule will be added to
  *
  * Avoid locking by allowing just one writer at a time in ima_write_policy()
  * Returns the length of the rule parsed, an error code on failure
  */
-ssize_t ima_parse_add_rule(char *rule)
+ssize_t ima_parse_add_rule(char *rule, struct ima_namespace *ima_ns)
 {
 	static const char op[] = "update_policy";
 	char *p;
 	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;
@@ -1577,7 +1576,7 @@ ssize_t ima_parse_add_rule(char *rule)
 
 	INIT_LIST_HEAD(&entry->list);
 
-	result = ima_parse_rule(p, entry);
+	result = ima_parse_rule(p, entry, ima_ns);
 	if (result) {
 		ima_free_rule(entry);
 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
@@ -1597,10 +1596,8 @@ ssize_t ima_parse_add_rule(char *rule)
  * different from the active one.  There is also only one user of
  * ima_delete_rules() at a time.
  */
-void ima_delete_rules(void)
+void ima_delete_rules(struct ima_namespace *ima_ns)
 {
-	/* 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;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 21/30] user namespace: Add function that checks if the UID map is defined
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (9 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 20/30] ima: Parse per ima namespace policy file krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 22/30] ima: Remap IDs of subject based rules if necessary krzysztof.struczynski
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 function that checks if the UID map is defined. It will be used by
ima to check if ID remapping in subject-based rules is necessary.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 include/linux/user_namespace.h |  6 ++++++
 kernel/user_namespace.c        | 11 +++++++++++
 2 files changed, 17 insertions(+)

diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index d9759c54fead..bcb21c41c910 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -138,6 +138,7 @@ extern bool in_userns(const struct user_namespace *ancestor,
 		       const struct user_namespace *child);
 extern bool current_in_userns(const struct user_namespace *target_ns);
 struct ns_common *ns_get_owner(struct ns_common *ns);
+extern bool userns_set_uidmap(const struct user_namespace *ns);
 #else
 
 static inline struct user_namespace *get_user_ns(struct user_namespace *ns)
@@ -182,6 +183,11 @@ static inline struct ns_common *ns_get_owner(struct ns_common *ns)
 {
 	return ERR_PTR(-EPERM);
 }
+
+static inline bool userns_set_uidmap(const struct user_namespace *ns)
+{
+	return true;
+}
 #endif
 
 #endif /* _LINUX_USER_H */
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 87804e0371fe..e38f9f11a589 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -1232,6 +1232,17 @@ bool current_in_userns(const struct user_namespace *target_ns)
 }
 EXPORT_SYMBOL(current_in_userns);
 
+bool userns_set_uidmap(const struct user_namespace *ns)
+{
+	bool mapping_defined;
+
+	mutex_lock(&userns_state_mutex);
+	mapping_defined = ns->uid_map.nr_extents != 0;
+	mutex_unlock(&userns_state_mutex);
+
+	return mapping_defined;
+}
+
 static inline struct user_namespace *to_user_ns(struct ns_common *ns)
 {
 	return container_of(ns, struct user_namespace, ns);
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 22/30] ima: Remap IDs of subject based rules if necessary
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (10 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 21/30] user namespace: Add function that checks if the UID map is defined krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 23/30] keys: Add domain tag to the keyring search criteria krzysztof.struczynski
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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>

If subject based rule is added to the policy before the user namespace
uid mapping is defined, ID has to be recalculated.

It can happen if the new user namespace is created alongside the new
ima namespace. The default policy rules are loaded when the first
process is born into the new ima namespace. In that case, user has no
chance to define the mapping. It can also happen for the custom policy
rules loaded from within the new ima namespace, before the mapping is
created.

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

diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index d4774eab6a98..bc1a4bb10bd0 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -18,6 +18,7 @@
 #include <linux/genhd.h>
 #include <linux/seq_file.h>
 #include <linux/ima.h>
+#include <linux/user_namespace.h>
 
 #include "ima.h"
 
@@ -78,6 +79,10 @@ struct ima_rule_entry {
 	char *fsname;
 	char *keyrings; /* Measure keys added to these keyrings */
 	struct ima_template_desc *template;
+	bool remap_uid; /* IDs of all subject oriented rules, added before the
+			 * user namespace mapping is defined,
+			 * have to be remapped.
+			 */
 };
 
 /*
@@ -484,6 +489,8 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
 			    const char *keyring)
 {
 	int i;
+	kuid_t remapped_kuid;
+	struct ima_namespace *current_ima_ns = get_current_ns();
 
 	if (func == KEY_CHECK) {
 		return (rule->flags & IMA_FUNC) && (rule->func == func) &&
@@ -507,21 +514,45 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
 	if ((rule->flags & IMA_FSUUID) &&
 	    !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid))
 		return false;
-	if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
-		return false;
+	if (rule->flags & IMA_UID) {
+		if (rule->remap_uid) {
+			remapped_kuid = make_kuid(current_ima_ns->user_ns,
+						  __kuid_val(rule->uid));
+			if (!uid_valid(remapped_kuid))
+				return false;
+		} else
+			remapped_kuid = rule->uid;
+		if (!rule->uid_op(cred->uid, remapped_kuid))
+			return false;
+	}
 	if (rule->flags & IMA_EUID) {
+		if (rule->remap_uid) {
+			remapped_kuid = make_kuid(current_ima_ns->user_ns,
+						  __kuid_val(rule->uid));
+			if (!uid_valid(remapped_kuid))
+				return false;
+		} else
+			remapped_kuid = rule->uid;
 		if (has_capability_noaudit(current, CAP_SETUID)) {
-			if (!rule->uid_op(cred->euid, rule->uid)
-			    && !rule->uid_op(cred->suid, rule->uid)
-			    && !rule->uid_op(cred->uid, rule->uid))
+			if (!rule->uid_op(cred->euid, remapped_kuid)
+			    && !rule->uid_op(cred->suid, remapped_kuid)
+			    && !rule->uid_op(cred->uid, remapped_kuid))
 				return false;
-		} else if (!rule->uid_op(cred->euid, rule->uid))
+		} else if (!rule->uid_op(cred->euid, remapped_kuid))
 			return false;
 	}
 
-	if ((rule->flags & IMA_FOWNER) &&
-	    !rule->fowner_op(inode->i_uid, rule->fowner))
-		return false;
+	if (rule->flags & IMA_FOWNER) {
+		if (rule->remap_uid) {
+			remapped_kuid = make_kuid(current_ima_ns->user_ns,
+						  __kuid_val(rule->fowner));
+			if (!uid_valid(remapped_kuid))
+				return false;
+		} else
+			remapped_kuid = rule->fowner;
+		if (!rule->fowner_op(inode->i_uid, remapped_kuid))
+			return false;
+	}
 	for (i = 0; i < MAX_LSM_RULES; i++) {
 		int rc = 0;
 		u32 osid;
@@ -701,6 +732,9 @@ static void add_rules(struct ima_namespace *ima_ns,
 
 	for (i = 0; i < count; i++) {
 		struct ima_rule_entry *entry;
+		bool set_uidmap;
+
+		set_uidmap = userns_set_uidmap(ima_ns->user_ns);
 
 		if (policy_rule & IMA_DEFAULT_POLICY) {
 			entry = &entries[i];
@@ -709,6 +743,9 @@ static void add_rules(struct ima_namespace *ima_ns,
 						GFP_KERNEL);
 				if (!entry)
 					continue;
+
+				if (!set_uidmap)
+					entry->remap_uid = true;
 			}
 
 			list_add_tail(&entry->list,
@@ -721,6 +758,9 @@ static void add_rules(struct ima_namespace *ima_ns,
 			if (!entry)
 				continue;
 
+			if (ima_ns != &init_ima_ns && !set_uidmap)
+				entry->remap_uid = true;
+
 			list_add_tail(&entry->list,
 				      &ima_ns->policy_data->ima_policy_rules);
 		}
@@ -1165,6 +1205,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
 	ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
 				       AUDIT_INTEGRITY_POLICY_RULE);
 
+	if ((ima_ns != &init_ima_ns) &&
+	    (!userns_set_uidmap(ima_ns->user_ns)))
+		entry->remap_uid = true;
+
 	entry->uid = INVALID_UID;
 	entry->fowner = INVALID_UID;
 	entry->uid_op = &uid_eq;
@@ -1396,8 +1440,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
 
 			result = kstrtoul(args[0].from, 10, &lnum);
 			if (!result) {
-				entry->uid = make_kuid(current_user_ns(),
-						       (uid_t) lnum);
+				if (!entry->remap_uid)
+					entry->uid =
+						make_kuid(current_user_ns(),
+							  (uid_t) lnum);
+				else
+					entry->uid = KUIDT_INIT((uid_t) lnum);
+
 				if (!uid_valid(entry->uid) ||
 				    (uid_t)lnum != lnum)
 					result = -EINVAL;
@@ -1424,8 +1473,16 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
 
 			result = kstrtoul(args[0].from, 10, &lnum);
 			if (!result) {
-				entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);
-				if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum))
+				if (!entry->remap_uid)
+					entry->fowner =
+						make_kuid(current_user_ns(),
+							  (uid_t) lnum);
+				else
+					entry->fowner =
+						KUIDT_INIT((uid_t) lnum);
+
+				if (!uid_valid(entry->fowner) ||
+				    (((uid_t)lnum) != lnum))
 					result = -EINVAL;
 				else
 					entry->flags |= IMA_FOWNER;
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 23/30] keys: Add domain tag to the keyring search criteria
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (11 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 22/30] ima: Remap IDs of subject based rules if necessary krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 24/30] keys: Include key domain tag in the iterative search krzysztof.struczynski
                   ` (6 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 24/30] keys: Include key domain tag in the iterative search
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (12 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 23/30] keys: Add domain tag to the keyring search criteria krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 25/30] keys: Allow to set key domain tag separately from the key type krzysztof.struczynski
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 domain tag to the key_match_data. If set, check domain tag in the
default match function and asymmetric keys match functions.

This will allow to use the key domain tag in the search criteria for
the iterative search, not only for the direct lookup that is based on
the index key.

Signed-off-by: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
---
 crypto/asymmetric_keys/asymmetric_type.c | 20 ++++++++++++++++----
 include/linux/key-type.h                 |  1 +
 security/keys/keyring.c                  | 10 +++++++++-
 3 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index 33e77d846caa..177429bc5c7b 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -249,9 +249,15 @@ static bool asymmetric_key_cmp(const struct key *key,
 {
 	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
 	const struct asymmetric_key_id *match_id = match_data->preparsed;
+	bool match;
 
-	return asymmetric_match_key_ids(kids, match_id,
-					asymmetric_key_id_same);
+	match = asymmetric_match_key_ids(kids, match_id,
+					 asymmetric_key_id_same);
+
+	if (match_data->domain_tag)
+		match &= key->index_key.domain_tag == match_data->domain_tag;
+
+	return match;
 }
 
 /*
@@ -262,9 +268,15 @@ static bool asymmetric_key_cmp_partial(const struct key *key,
 {
 	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
 	const struct asymmetric_key_id *match_id = match_data->preparsed;
+	bool match;
+
+	match = asymmetric_match_key_ids(kids, match_id,
+					 asymmetric_key_id_partial);
+
+	if (match_data->domain_tag)
+		match &= key->index_key.domain_tag == match_data->domain_tag;
 
-	return asymmetric_match_key_ids(kids, match_id,
-					asymmetric_key_id_partial);
+	return match;
 }
 
 /*
diff --git a/include/linux/key-type.h b/include/linux/key-type.h
index 2ab2d6d6aeab..c8ea26ab242c 100644
--- a/include/linux/key-type.h
+++ b/include/linux/key-type.h
@@ -55,6 +55,7 @@ struct key_match_data {
 	unsigned	lookup_type;	/* Type of lookup for this search. */
 #define KEYRING_SEARCH_LOOKUP_DIRECT	0x0000	/* Direct lookup by description. */
 #define KEYRING_SEARCH_LOOKUP_ITERATE	0x0001	/* Iterative search. */
+	struct key_tag  *domain_tag;    /* Key domain tag */
 };
 
 /*
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 12583241ff63..7e45e534035f 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -565,7 +565,13 @@ int restrict_link_reject(struct key *keyring,
 bool key_default_cmp(const struct key *key,
 		     const struct key_match_data *match_data)
 {
-	return strcmp(key->description, match_data->raw_data) == 0;
+	bool match;
+
+	match = strcmp(key->description, match_data->raw_data) == 0;
+	if (match_data->domain_tag)
+		match &= key->index_key.domain_tag == match_data->domain_tag;
+
+	return match;
 }
 
 /*
@@ -957,6 +963,8 @@ key_ref_t keyring_search_tag(key_ref_t keyring,
 
 	if (recurse)
 		ctx.flags |= KEYRING_SEARCH_RECURSE;
+	if (domain_tag)
+		ctx.match_data.domain_tag = domain_tag;
 	if (type->match_preparse) {
 		ret = type->match_preparse(&ctx.match_data);
 		if (ret < 0)
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [RFC PATCH 25/30] keys: Allow to set key domain tag separately from the key type
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (13 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 24/30] keys: Include key domain tag in the iterative search krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 26/30] ima: Add key domain to the ima namespace krzysztof.struczynski
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 26/30] ima: Add key domain to the ima namespace
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (14 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 25/30] keys: Allow to set key domain tag separately from the key type krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 27/30] integrity: Add key domain tag to the search criteria krzysztof.struczynski
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 27/30] integrity: Add key domain tag to the search criteria
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (15 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 26/30] ima: Add key domain to the ima namespace krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 28/30] ima: Load per ima namespace x509 certificate krzysztof.struczynski
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 28/30] ima: Load per ima namespace x509 certificate
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (16 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 27/30] integrity: Add key domain tag to the search criteria krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 29/30] ima: Add dummy boot aggregate to per ima namespace measurement list krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 30/30] ima: Set ML template per ima namespace krzysztof.struczynski
  19 siblings, 0 replies; 21+ messages in thread
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>

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	[flat|nested] 21+ messages in thread

* [RFC PATCH 29/30] ima: Add dummy boot aggregate to per ima namespace measurement list
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (17 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 28/30] ima: Load per ima namespace x509 certificate krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  2020-08-18 15:42 ` [RFC PATCH 30/30] ima: Set ML template per ima namespace krzysztof.struczynski
  19 siblings, 0 replies; 21+ messages in thread
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 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	[flat|nested] 21+ messages in thread

* [RFC PATCH 30/30] ima: Set ML template per ima namespace
  2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
                   ` (18 preceding siblings ...)
  2020-08-18 15:42 ` [RFC PATCH 29/30] ima: Add dummy boot aggregate to per ima namespace measurement list krzysztof.struczynski
@ 2020-08-18 15:42 ` krzysztof.struczynski
  19 siblings, 0 replies; 21+ messages in thread
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>

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	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2020-08-18 16:02 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-18 15:42 [RFC PATCH 10/30] ima: Add ima namespace ID to the ima ML related structures krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 11/30] ima: Keep track of the measurment list per ima namespace krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 12/30] ima: Check ima namespace ID during digest entry lookup krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 13/30] ima: Add a new ima template that includes namespace ID krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 14/30] ima: Add per namespace view of the measurement list krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 15/30] ima: Add a reader counter to the integrity inode data krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 16/30] ima: Extend permissions to the ima securityfs entries krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 17/30] ima: Add the violation counter to the namespace krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 18/30] ima: Change the owning user namespace of the ima namespace if necessary krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 19/30] ima: Configure the new ima namespace from securityfs krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 20/30] ima: Parse per ima namespace policy file krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 21/30] user namespace: Add function that checks if the UID map is defined krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 22/30] ima: Remap IDs of subject based rules if necessary krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 23/30] keys: Add domain tag to the keyring search criteria krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 24/30] keys: Include key domain tag in the iterative search krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 25/30] keys: Allow to set key domain tag separately from the key type krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 26/30] ima: Add key domain to the ima namespace krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 27/30] integrity: Add key domain tag to the search criteria krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 28/30] ima: Load per ima namespace x509 certificate krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 29/30] ima: Add dummy boot aggregate to per ima namespace measurement list krzysztof.struczynski
2020-08-18 15:42 ` [RFC PATCH 30/30] ima: Set ML template per ima namespace krzysztof.struczynski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).