diff mbox

[3/3] ima: add pre-calculated measurements (experimental)

Message ID 1466602505-21915-4-git-send-email-zohar@linux.vnet.ibm.com
State New, archived
Headers show

Commit Message

Mimi Zohar June 22, 2016, 1:35 p.m. UTC
This patch defines a new IMA hook named ima_add_measurement_check()
for including pre-calculated measurements in the IMA measurement list.

Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
---
 Documentation/ABI/testing/ima_policy |   2 +-
 include/linux/ima.h                  |  12 ++++
 security/integrity/ima/Kconfig       |   8 +++
 security/integrity/ima/ima.h         |   1 +
 security/integrity/ima/ima_buffer.c  | 116 +++++++++++++++++++++++++++++------
 security/integrity/ima/ima_policy.c  |  18 +++++-
 6 files changed, 136 insertions(+), 21 deletions(-)
diff mbox

Patch

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
index 5a99c6f..e5a137e 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -28,7 +28,7 @@  Description:
 		base: 	func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK]
 				[FIRMWARE_CHECK]
 				[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
-				[KEXEC_CMDLINE_CHECK]
+				[KEXEC_CMDLINE_CHECK] [PRECALC_CHECK]
 			mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
 			       [[^]MAY_EXEC]
 			fsmagic:= hex value
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 88203f9..797de51 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -15,6 +15,7 @@  struct linux_binprm;
 
 enum ima_buffer_id {
 	MEASURING_KEXEC_CMDLINE,
+	MEASURING_PRECALC_DATA,
 	MEASURING_MAX_BUFFER_ID
 };
 
@@ -29,6 +30,9 @@  extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
 extern void ima_post_path_mknod(struct dentry *dentry);
 extern void ima_buffer_check(void *buf, loff_t size,
 			     enum ima_buffer_id buffer_id);
+extern void ima_add_measurement_check(const char *hashname, u8 *digest,
+				      loff_t size, enum ima_buffer_id buffer_id,
+				      char *hint);
 
 #else
 static inline int ima_bprm_check(struct linux_binprm *bprm)
@@ -72,6 +76,14 @@  static inline void ima_buffer_check(void *buf, loff_t size,
 {
 	return;
 }
+
+static inline void ima_add_measurement_check(const char *hashname, u8 *digest,
+					     loff_t size,
+					     enum ima_buffer_id buffer_id,
+					     char *hint)
+{
+	return;
+}
 #endif /* CONFIG_IMA */
 
 #ifdef CONFIG_IMA_APPRAISE
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 5487827..0fb54d3 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -44,6 +44,14 @@  config IMA_LSM_RULES
 	help
 	  Disabling this option will disregard LSM based policy rules.
 
+config IMA_PRECALC_RULES
+	bool "Permit pre-calculated measurements (EXPERIMENTAL)"
+	depends on IMA
+	default n
+	help
+	  Enabling this option will permit pre-calculated measurements
+	  to be added to the IMA measurement list.
+
 choice
 	prompt "Default template"
 	default IMA_NG_TEMPLATE
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 5f21a9a..ccad21d 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -152,6 +152,7 @@  enum ima_hooks {
 	KEXEC_INITRAMFS_CHECK,
 	KEXEC_CMDLINE_CHECK,
 	POLICY_CHECK,
+	PRECALC_CHECK,
 	MAX_CHECK
 };
 
diff --git a/security/integrity/ima/ima_buffer.c b/security/integrity/ima/ima_buffer.c
index e74131b..ad49f6c 100644
--- a/security/integrity/ima/ima_buffer.c
+++ b/security/integrity/ima/ima_buffer.c
@@ -11,6 +11,8 @@ 
 #include <linux/uaccess.h>
 #include <linux/module.h>
 #include <linux/ima.h>
+#include <crypto/hash_info.h>
+#include <linux/string_helpers.h>
 
 #include "ima.h"
 
@@ -21,9 +23,37 @@  struct buffer_idmap {
 
 static struct buffer_idmap _idmap[MEASURING_MAX_BUFFER_ID] = {
 	[MEASURING_KEXEC_CMDLINE].func = KEXEC_CMDLINE_CHECK,
-	[MEASURING_KEXEC_CMDLINE].buf = "boot-cmdline",
+	[MEASURING_KEXEC_CMDLINE].buf = "kexec-boot-cmdline",
+	[MEASURING_PRECALC_DATA].func = PRECALC_CHECK,
+	[MEASURING_PRECALC_DATA].buf = "precalc",
 };
 
+#define IMA_MAX_BUFFER_HINT_SIZE 255
+
+static int store_buffer_measurement(struct ima_digest_data *hash, int pcr,
+				    char *buffer_hint)
+{
+	struct ima_template_entry *entry;
+	struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
+	struct ima_event_data event_data = {iint, NULL, NULL, NULL, 0, NULL};
+	int violation = 0;
+	int result;
+
+	iint->ima_hash = hash;
+	event_data.filename = buffer_hint;
+
+	result = ima_alloc_init_template(&event_data, &entry);
+	if (result < 0)
+		return result;
+
+	result = ima_store_template(entry, violation, NULL,
+				    event_data.filename, pcr);
+	if (result < 0)
+		ima_free_template_entry(entry);
+
+	return result;
+}
+
 static void process_buffer_measurement(void *buf, loff_t size,
 				       enum ima_buffer_id buffer_id, int pcr)
 {
@@ -31,10 +61,6 @@  static void process_buffer_measurement(void *buf, loff_t size,
 		struct ima_digest_data hdr;
 		char digest[IMA_MAX_DIGEST_SIZE];
 	} hash;
-	struct ima_template_entry *entry;
-	struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
-	struct ima_event_data event_data = {iint, NULL, NULL, NULL, 0, NULL};
-	int violation = 0;
 	int result;
 
 	memset(&hash, 0, sizeof(hash));
@@ -45,20 +71,10 @@  static void process_buffer_measurement(void *buf, loff_t size,
 		return;
 	}
 
-	iint->ima_hash = &hash.hdr;
-	event_data.filename = _idmap[buffer_id].buf;
-	result = ima_alloc_init_template(&event_data, &entry);
-	if (result < 0) {
-		pr_debug("failed allocating template\n");
-		return;
-	}
-
-	result = ima_store_template(entry, violation, NULL,
-				    event_data.filename, pcr);
-	if (result < 0) {
+	result = store_buffer_measurement(&hash.hdr, pcr,
+					  _idmap[buffer_id].buf);
+	if (result < 0)
 		pr_debug("failed storing buffer measurement\n");
-		ima_free_template_entry(entry);
-	}
 }
 
 /**
@@ -82,3 +98,67 @@  void ima_buffer_check(void *buf, loff_t size, enum ima_buffer_id buffer_id)
 	process_buffer_measurement(buf, size, buffer_id, pcr);
 }
 EXPORT_SYMBOL_GPL(ima_buffer_check);
+
+/**
+ * ima_add_measurement_check - add pre-calculated hash measurement
+ * @hashname: pointer to hash algorithm name
+ * @digest: pointer to hash digest
+ * @size: hash digest size
+ * @buffer_id: caller identifier
+ * @hint: measurement identifier
+ *
+ * Include pre-calculated hash measurements in the IMA measurement list.
+ */
+void ima_add_measurement_check(const char *hashname, u8 *digest, loff_t size,
+			      enum ima_buffer_id buffer_id, char *hint)
+{
+	struct {
+		struct ima_digest_data hdr;
+		char digest[IMA_MAX_DIGEST_SIZE];
+	} hash;
+	int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+	char buffer_hint[IMA_MAX_BUFFER_HINT_SIZE];
+	char *buf;
+	int result, i;
+
+	if (buffer_id > MEASURING_MAX_BUFFER_ID)
+		return;
+
+	if (!ima_match_buffer_id(_idmap[buffer_id].func, &pcr))
+		return;
+
+	if (!hint) {
+		pr_debug("missing buffer hint\n");
+		return;
+	}
+
+	buf = kstrdup_quotable(hint, GFP_KERNEL);
+	if (!buf) {
+		pr_debug("failed quoting buffer hint\n");
+		return;
+	}
+
+	/* Limit the total measurement hint to IMA_MAX_BUFFER_HINT_SIZE. */
+	snprintf(buffer_hint, sizeof(buffer_hint), "(%s) %s",
+		 _idmap[buffer_id].buf, buf);
+	kfree(buf);
+
+	memset(&hash, 0, sizeof(hash));
+	for (i = 1; i < HASH_ALGO__LAST; i++) {
+		if (strcmp(hashname, hash_algo_name[i]) != 0)
+			continue;
+		hash.hdr.algo = i;
+		break;
+	}
+	if (hash.hdr.algo == 0) {
+		pr_debug("invalid hash algorithm (%d)\n", hash.hdr.algo);
+		return;
+	}
+
+	hash.hdr.length = size;
+	memcpy(&hash.hdr.digest, digest, size);
+	result = store_buffer_measurement(&hash.hdr, pcr, buffer_hint);
+	if (result < 0)
+		pr_debug("failed to store buffer measurement\n");
+}
+EXPORT_SYMBOL_GPL(ima_add_measurement_check);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 8e53f84..4094dd7 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -20,6 +20,7 @@ 
 #include <linux/rculist.h>
 #include <linux/genhd.h>
 #include <linux/seq_file.h>
+#include <linux/ima.h>
 
 #include "ima.h"
 
@@ -54,6 +55,12 @@  enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
 
 enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB };
 
+#ifdef CONFIG_MEASURING_PRECALC_RULES
+static int permit_measuring_precalc_rules = 1;
+#else
+static int permit_measuring_precalc_rules;
+#endif
+
 struct ima_rule_entry {
 	struct list_head list;
 	int action;
@@ -668,6 +675,9 @@  static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 				entry->func = KEXEC_CMDLINE_CHECK;
 			else if (strcmp(args[0].from, "POLICY_CHECK") == 0)
 				entry->func = POLICY_CHECK;
+			else if ((strcmp(args[0].from, "PRECALC_CHECK") == 0)
+				 && permit_measuring_precalc_rules)
+				entry->func = PRECALC_CHECK;
 			else
 				result = -EINVAL;
 			if (!result)
@@ -929,7 +939,7 @@  enum {
 	func_file = 0, func_mmap, func_bprm,
 	func_module, func_firmware, func_post,
 	func_kexec_kernel, func_kexec_initramfs,
-	func_kexec_cmdline, func_policy
+	func_kexec_cmdline, func_policy, func_precalc
 };
 
 static char *func_tokens[] = {
@@ -942,7 +952,8 @@  static char *func_tokens[] = {
 	"KEXEC_KERNEL_CHECK",
 	"KEXEC_INITRAMFS_CHECK",
 	"KEXEC_CMDLINE_CHECK",
-	"POLICY_CHECK"
+	"POLICY_CHECK",
+	"PRECALC_CHECK"
 };
 
 void *ima_policy_start(struct seq_file *m, loff_t *pos)
@@ -1019,6 +1030,9 @@  static void policy_func_show(struct seq_file *m, enum ima_hooks func)
 	case POLICY_CHECK:
 		seq_printf(m, pt(Opt_func), ft(func_policy));
 		break;
+	case PRECALC_CHECK:
+		seq_printf(m, pt(Opt_func), ft(func_precalc));
+		break;
 	default:
 		snprintf(tbuf, sizeof(tbuf), "%d", func);
 		seq_printf(m, pt(Opt_func), tbuf);