All of lore.kernel.org
 help / color / mirror / Atom feed
From: prsriva02@gmail.com
To: linux-integrity@vger.kernel.org
Cc: zohar@linux.ibm.com, Prakhar Srivastava <prsriva@microsoft.com>
Subject: [PATCH] kexec_buffer measure
Date: Fri, 12 Apr 2019 11:08:29 -0700	[thread overview]
Message-ID: <20190412180829.15631-1-prsriva02@gmail.com> (raw)

From: Prakhar Srivastava <prsriva@microsoft.com>

This adds a generic buffer measure ima function hook.
The Buffer passed will be added to the sig field of the template if used.
The hash<algo configured>(buffer) is added as the IMA measurements, along side the buffer itself in hex.
      For cmdline: kernel file name is prefixed to the cmdline to distinguish between callers.
An enum is used to control what all buffers can be written to it.

Verified How:
Replaced kernel on machine.
read ima_measurement_log
called kexec -s <with cmdline>
read ima_measurement_log
     - A new entry for the cmdline passed can be seen.
     - HEX to text verified: http://www.unit-conversion.info/texttools/hexadecimal/
     - Generated Hash for the text buffer and matched it with in IMA log

Signed-off-by: Prakhar Srivastava <prsriva@microsoft.com>
---
 include/linux/ima.h               |  21 ++++-
 kernel/kexec_core.c               |  68 +++++++++++++++
 kernel/kexec_file.c               |  18 ++++
 kernel/kexec_internal.h           |   6 +-
 security/integrity/ima/Kconfig    |  16 ++++
 security/integrity/ima/ima_main.c | 135 ++++++++++++++++++++++++++++++
 security/integrity/integrity.h    |   3 +
 7 files changed, 264 insertions(+), 3 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 7f6952f8d6aa..46c3b95b2637 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -14,6 +14,14 @@
 #include <linux/kexec.h>
 struct linux_binprm;
 
+#ifdef CONFIG_IMA_BUFFER_MEASURE
+enum buffer_id{
+	KEXEC_CMDLINE = 1
+	// other buffer ids
+	// KERNEL_VERSION
+};
+#endif
+
 #ifdef CONFIG_IMA
 extern int ima_bprm_check(struct linux_binprm *bprm);
 extern int ima_file_check(struct file *file, int mask, int opened);
@@ -23,7 +31,10 @@ extern int ima_read_file(struct file *file, enum kernel_read_file_id id);
 extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
 			      enum kernel_read_file_id id);
 extern void ima_post_path_mknod(struct dentry *dentry);
-
+#ifdef CONFIG_IMA_BUFFER_MEASURE
+extern int ima_add_buffer_measure(const void *buff,
+				loff_t size, enum buffer_id id);
+#endif
 #ifdef CONFIG_IMA_KEXEC
 extern void ima_add_kexec_buffer(struct kimage *image);
 #endif
@@ -64,7 +75,13 @@ static inline void ima_post_path_mknod(struct dentry *dentry)
 {
 	return;
 }
-
+#ifndef CONFIG_IMA_BUFFER_MEASURE
+static inline int ima_add_buffer_measure(const void *buff,
+					loff_t size, enum buffer_id id)
+{
+	return 0;
+}
+#endif /* CONFIG_IMA_BUFFER_MEASURE */
 #endif /* CONFIG_IMA */
 
 #ifndef CONFIG_IMA_KEXEC
diff --git a/kernel/kexec_core.c b/kernel/kexec_core.c
index ae1a3ba24df5..7cf795794fda 100644
--- a/kernel/kexec_core.c
+++ b/kernel/kexec_core.c
@@ -1151,3 +1151,71 @@ void __weak arch_kexec_protect_crashkres(void)
 
 void __weak arch_kexec_unprotect_crashkres(void)
 {}
+
+#ifdef CONFIG_MEASURE_KEXEC_CMDLINE
+/**
+ * kexec_cmdline_prepend_img_name - prepare the buffer with cmdline
+ * that needs to be measured
+ * @outbuf - out buffer that contains the formated string
+ * @kernel_fd - the fild identifier for the kerenel image
+ * @cmdline_ptr - ptr to the cmdline buffer
+ * @cmdline_len - len of the buffer.
+ * 
+ * This generates a buffer in the format Kerenelfilename::cmdline
+ * 
+ * On success return 0.
+ * On failure return -EINVAL.
+*/
+int kexec_cmdline_prepend_img_name(char **outbuf, int kernel_fd,
+				const char __user *cmdline_ptr,
+				unsigned long cmdline_len)
+{
+	int ret = -EINVAL;
+	struct fd f = {};
+	int size = 0;
+	char *buf = NULL;
+	char *temp_buf = NULL;
+	char delimiter[] = "::";
+
+	if (!outbuf)
+		goto out;
+
+	f = fdget(kernel_fd);
+	if (!f.file)
+		goto out;
+
+	size = (f.file->f_path.dentry->d_name.len + cmdline_len - 1+
+			ARRAY_SIZE(delimiter)) -1;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf)
+		goto out;
+
+	temp_buf = memdup_user(cmdline_ptr, cmdline_len);
+	if (IS_ERR(temp_buf)) {
+		kfree(buf);
+		ret = PTR_ERR(temp_buf);
+		goto out;
+	}
+
+	memcpy(buf, f.file->f_path.dentry->d_name.name,
+		f.file->f_path.dentry->d_name.len);
+	memcpy(buf + f.file->f_path.dentry->d_name.len,
+		delimiter, ARRAY_SIZE(delimiter) -1);
+	memcpy(buf + f.file->f_path.dentry->d_name.len +
+		ARRAY_SIZE(delimiter) - 1,
+		temp_buf, cmdline_len -1);
+
+	*outbuf = buf;
+	ret = size;
+
+	pr_debug("kexec cmdline buff: %s\n",buf);
+
+out:
+	if (f.file)
+		fdput(f);
+
+	kfree(temp_buf);
+	return ret;
+}
+#endif
\ No newline at end of file
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b118735fea9d..a3a839f2710d 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -126,6 +126,10 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 	int ret = 0;
 	void *ldata;
 	loff_t size;
+#ifdef CONFIG_MEASURE_KEXEC_CMDLINE
+	char *buff_to_measure = NULL;
+	int buff_to_measure_size = 0;
+#endif
 
 	ret = kernel_read_file_from_fd(kernel_fd, &image->kernel_buf,
 				       &size, INT_MAX, READING_KEXEC_IMAGE);
@@ -133,6 +137,16 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 		return ret;
 	image->kernel_buf_len = size;
 
+#ifdef CONFIG_MEASURE_KEXEC_CMDLINE
+	/* IMA measures the cmdline passed to the next kernel*/
+	buff_to_measure_size = kexec_cmdline_prepend_img_name(&buff_to_measure,
+					kernel_fd, cmdline_ptr, cmdline_len);
+
+	if (buff_to_measure_size > 0)
+		ima_add_buffer_measure(buff_to_measure, buff_to_measure_size,
+				KEXEC_CMDLINE);
+#endif
+
 	/* IMA needs to pass the measurement list to the next kernel. */
 	ima_add_kexec_buffer(image);
 
@@ -195,6 +209,10 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
 	image->image_loader_data = ldata;
 out:
 	/* In case of error, free up all allocated memory in this function */
+#ifdef CONFIG_MEASURE_KEXEC_CMDLINE
+	kfree(buff_to_measure);
+#endif
+
 	if (ret)
 		kimage_file_post_load_cleanup(image);
 	return ret;
diff --git a/kernel/kexec_internal.h b/kernel/kexec_internal.h
index 799a8a452187..808aa2330cdb 100644
--- a/kernel/kexec_internal.h
+++ b/kernel/kexec_internal.h
@@ -11,7 +11,11 @@ int kimage_load_segment(struct kimage *image, struct kexec_segment *segment);
 void kimage_terminate(struct kimage *image);
 int kimage_is_destination_range(struct kimage *image,
 				unsigned long start, unsigned long end);
-
+#ifdef CONFIG_MEASURE_KEXEC_CMDLINE
+int kexec_cmdline_prepend_img_name(char **outbuf, int kernel_fd,
+				const char __user *cmdline_ptr,
+				unsigned long cmdline_len);
+#endif
 extern struct mutex kexec_mutex;
 
 #ifdef CONFIG_KEXEC_FILE
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
index 370eb2f4dd37..349e5a818a5f 100644
--- a/security/integrity/ima/Kconfig
+++ b/security/integrity/ima/Kconfig
@@ -219,3 +219,19 @@ config IMA_APPRAISE_SIGNED_INIT
 	default n
 	help
 	   This option requires user-space init to be signed.
+
+config IMA_BUFFER_MEASURE
+	bool "IMA to measure buffers"
+	depends on IMA=y
+	depends on CRYPTO=y
+	default n
+	help
+	  This enables buffer measurement in ima.
+
+config MEASURE_KEXEC_CMDLINE
+	bool "Measure cmdline passed to KEXEC"
+	depends on KEXEC_FILE=y
+	depends on IMA_BUFFER_MEASURE =y
+	default n
+	help
+		This measures the cmldine passed to kexec_file_load call.
\ No newline at end of file
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2aebb7984437..6d1335601672 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -416,6 +416,141 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 	return process_measurement(file, buf, size, MAY_READ, func, 0);
 }
 
+#ifdef CONFIG_IMA_BUFFER_MEASURE
+/**
+ * ima_update_template_sig_field - update the sig field with the
+ * buffer passed.
+ * @entry - template entry that needs to be edited.
+ * @buff - biffer that needs to be added to the sig field
+ * @size - length of the buffer
+ * 
+ * update the sig field of the template to contain the buffer.
+ * 
+*/
+void ima_update_template_sig_field(struct ima_template_entry *entry,
+			const void *buff, loff_t size)
+{
+	int field_index = 0;
+	struct buffer_data{
+		uint8_t type;			/* xattr type */
+		uint32_t buff_len;		/* Length of buffer added */
+		char data[0];			/* Data buffer*/
+	};
+	struct buffer_data tmp_buff , *string_data = &tmp_buff;
+
+	string_data = kzalloc(size + sizeof(*string_data), GFP_KERNEL);
+	if(IS_ERR(string_data))
+		goto out;
+
+	if(!entry || !buff || size ==0)
+		goto out;
+
+	string_data->type = IMA_BUFFER_ENCODED;
+	string_data->buff_len = size;
+	memcpy(string_data->data, buff, size);
+
+	for(field_index = 0; field_index < entry->template_desc->num_fields ;
+			field_index++)
+	{
+		if(strcmp(entry->template_desc->fields[field_index]->field_id, "sig")
+			 == 0)
+		{
+			entry->template_data[field_index].len =
+					sizeof(*string_data) + string_data->buff_len;
+			entry->template_data[field_index].data =
+					kzalloc(entry->template_data[field_index].len, GFP_KERNEL);
+			if(IS_ERR(entry->template_data[field_index].data))
+			{
+				entry->template_data[field_index].len = 0;
+				kfree(entry->template_data[field_index].data);
+				goto out;
+			}
+			memcpy(entry->template_data[field_index].data, string_data ,
+				entry->template_data[field_index].len);
+		}
+		
+	}
+
+out:
+	kfree(string_data);
+}
+
+/**
+ * ima_add_buffer_measure - Measure the buffer passed to ima log.
+ * (Instead of using the file hash the buffer hash is used).
+ * @buff - The buffer that needs to be added to the log
+ * @size - size of buffer(in bytes)
+ * @id - buffer id, this is differentiator for the various buffers
+ * that can be measured.
+ * 
+ * The buffer passed is added to the ima logs.
+ * If the sig template is used, then the sig field contains the buffer.
+ * 
+ * On success return 0.
+ * On error cases surface errors from ima calls.
+ */
+int ima_add_buffer_measure(const void *buff, loff_t size,
+				 enum buffer_id id)
+{
+	int ret = -EINVAL;
+	struct ima_template_entry *entry = NULL;
+	struct integrity_iint_cache tmp_iint, *iint = &tmp_iint;
+	struct ima_event_data event_data = {iint, NULL, NULL,
+					    NULL, 0, NULL};
+	struct {
+		struct ima_digest_data hdr;
+		char digest[IMA_MAX_DIGEST_SIZE];
+	} hash;
+
+	int violation = 0;
+	char *name = NULL;
+
+	if (!buff || size ==0)
+		goto err_out;
+
+	switch (id) {
+		case KEXEC_CMDLINE:
+			name = "kexec-cmdline";
+			break;
+		default:
+			name = "unknown";
+			goto err_out;
+	}
+
+	memset(iint, 0, sizeof(*iint));
+	memset(&hash, 0, sizeof(hash));
+
+	event_data.filename = name;
+
+	iint->ima_hash = &hash.hdr;
+	iint->ima_hash->algo = ima_hash_algo;
+	iint->ima_hash->length = hash_digest_size[ima_hash_algo];
+
+	ret = ima_calc_buffer_hash(buff, size, iint->ima_hash);
+	if (ret < 0)
+		goto err_out;
+
+	ret = ima_alloc_init_template(&event_data, &entry);
+	if (ret < 0)
+		goto err_out;
+
+	ima_update_template_sig_field(entry, buff, size);
+
+	ret = ima_store_template(entry, violation, NULL,
+					buff, CONFIG_IMA_MEASURE_PCR_IDX);
+	if (ret < 0) {
+		ima_free_template_entry(entry);
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	pr_err("Error in adding buffer measure: %d\n", ret);
+	return ret;
+}
+#endif
+
 static int __init init_ima(void)
 {
 	int error;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 24520b4ef3b0..85efa38b9f83 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -58,6 +58,9 @@ enum evm_ima_xattr_type {
 	EVM_XATTR_HMAC,
 	EVM_IMA_XATTR_DIGSIG,
 	IMA_XATTR_DIGEST_NG,
+#ifdef CONFIG_IMA_BUFFER_MEASURE
+	IMA_BUFFER_ENCODED,
+#endif
 	IMA_XATTR_LAST
 };
 
-- 
2.17.1


             reply	other threads:[~2019-04-12 18:08 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-04-12 18:08 prsriva02 [this message]
2019-04-15 21:38 ` [PATCH] kexec_buffer measure Mimi Zohar
2019-04-20  0:30 prakhar srivastava
2019-04-23  0:18 ` Mimi Zohar
2019-05-02 15:48   ` Mimi Zohar
2019-05-02 16:26     ` Casey Schaufler
2019-05-02 16:28     ` Casey Schaufler
2019-05-03  0:53       ` Tetsuo Handa
2019-05-03 14:24         ` Mimi Zohar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190412180829.15631-1-prsriva02@gmail.com \
    --to=prsriva02@gmail.com \
    --cc=linux-integrity@vger.kernel.org \
    --cc=prsriva@microsoft.com \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.