lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20201031165122.21539-5-dpsmith@apertussolutions.com>
Date:   Sat, 31 Oct 2020 12:51:22 -0400
From:   "Daniel P. Smith" <dpsmith@...rtussolutions.com>
To:     linux-kernel@...r.kernel.org, x86@...nel.org,
        linux-integrity@...r.kernel.org
Cc:     ross.philipson@...cle.com, dpsmith@...rtussolutions.com,
        jarkko.sakkinen@...ux.intel.com, tglx@...utronix.de,
        mingo@...hat.com, bp@...en8.de, hpa@...or.com, luto@...capital.net,
        trenchboot-devel@...glegroups.com
Subject: [RFC PATCH 4/4] x86: Add early PCR extend support for Secure Launch

Access to PCR extend functionality is needed early in the compressed
kernel so that Secure Launch can measure items into the DRTM PCRs
before these items are used. The items include the boot parameters and
associated information, the kernel command line, any external initrd
and the OS-MLE TXT heap structure.

NOTE: for the RFC, early_pcr_extend.c is built unconditionally in the
Makefile. In the full Secure Launch patch set it is conditionally built
if CONFIG_SECURE_LAUNCH is defined.

Signed-off-by: Daniel P. Smith <dpsmith@...rtussolutions.com>
Signed-off-by: Ross Philipson <ross.philipson@...cle.com>
---
 arch/x86/boot/compressed/Makefile           |   2 +
 arch/x86/boot/compressed/early_pcr_extend.c | 311 ++++++++++++++++++++++++++++
 arch/x86/boot/compressed/early_pcr_extend.h |  92 ++++++++
 3 files changed, 405 insertions(+)
 create mode 100644 arch/x86/boot/compressed/early_pcr_extend.c
 create mode 100644 arch/x86/boot/compressed/early_pcr_extend.h

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 5a828fde7a42..8f0b29dce9da 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -93,6 +93,8 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
 vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
 efi-obj-$(CONFIG_EFI_STUB) = $(objtree)/drivers/firmware/efi/libstub/lib.a
 
+vmlinux-objs-y += $(obj)/early_pcr_extend.o
+
 # The compressed kernel is built with -fPIC/-fPIE so that a boot loader
 # can place it anywhere in memory and it will still run. However, since
 # it is executed as-is without any ELF relocation processing performed
diff --git a/arch/x86/boot/compressed/early_pcr_extend.c b/arch/x86/boot/compressed/early_pcr_extend.c
new file mode 100644
index 000000000000..94ee3cc9814e
--- /dev/null
+++ b/arch/x86/boot/compressed/early_pcr_extend.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@...rtussolutions.com>
+ *
+ * The code in this file is based on the article "Writing a TPM Device Driver"
+ * published on http://ptgmedia.pearsoncmg.com.
+ *
+ * The scope of the TPM functionality here is solely to allow DRTM PCRs to be
+ * extended early in the compressed kernel.
+ */
+
+#include <linux/types.h>
+#include <linux/bits.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#define COMPRESSED_KERNEL
+#include <crypto/sha.h>
+#include <linux/tpm_buffer.h>
+#include <linux/tpm_command.h>
+#include <linux/tpm_core.h>
+#include "../../../../drivers/char/tpm/tpm_tis_defs.h"
+#include "early_pcr_extend.h"
+
+#define tpm_read8(f) readb((void *)(u64)(TPM_MMIO_BASE | f))
+#define tpm_write8(v, f) writeb(v, (void *)(u64)(TPM_MMIO_BASE | f))
+#define tpm_read32(f) readl((void *)(u64)(TPM_MMIO_BASE | f));
+
+static struct tpm tpm;
+static u8 locality = TPM_NO_LOCALITY;
+
+static void tpm_io_delay(void)
+{
+	/* This is the default delay type in native_io_delay */
+	asm volatile ("outb %al, $0x80");
+}
+
+static void tpm_udelay(int loops)
+{
+	while (loops--)
+		tpm_io_delay();	/* Approximately 1 us */
+}
+
+static u32 burst_wait(void)
+{
+	u32 count = 0;
+
+	while (count == 0) {
+		count = tpm_read8(TPM_STS(locality) + 1);
+		count += tpm_read8(TPM_STS(locality) + 2) << 8;
+
+		/* Wait for FIFO to drain */
+		if (count == 0)
+			tpm_udelay(TPM_BURST_MIN_DELAY);
+	}
+
+	return count;
+}
+
+static void tis_relinquish_locality(void)
+{
+	if (locality < TPM_MAX_LOCALITY)
+		tpm_write8(TPM_ACCESS_ACTIVE_LOCALITY, TPM_ACCESS(locality));
+
+	locality = TPM_NO_LOCALITY;
+}
+
+static u8 tis_request_locality(u8 l)
+{
+	if (l > TPM_MAX_LOCALITY)
+		return TPM_NO_LOCALITY;
+
+	if (l == locality)
+		return locality;
+
+	tis_relinquish_locality();
+
+	tpm_write8(TPM_ACCESS_REQUEST_USE, TPM_ACCESS(l));
+
+	/* wait for locality to be granted */
+	if (tpm_read8(TPM_ACCESS(l)) & TPM_ACCESS_ACTIVE_LOCALITY)
+		locality = l;
+
+	return locality;
+}
+
+static size_t tis_send(struct tpm_buf *buf)
+{
+	u8 status, *buf_ptr;
+	u32 length, count = 0, burstcnt = 0;
+
+	if (locality > TPM_MAX_LOCALITY)
+		return 0;
+
+	for (status = 0; (status & TPM_STS_COMMAND_READY) == 0; ) {
+		tpm_write8(TPM_STS_COMMAND_READY, TPM_STS(locality));
+		status = tpm_read8(TPM_STS(locality));
+	}
+
+	buf_ptr = buf->data;
+	length = tpm_buf_length(buf);
+
+	/* send all but the last byte */
+	while (count < (length - 1)) {
+		burstcnt = burst_wait();
+		for (; burstcnt > 0 && count < (length - 1); burstcnt--) {
+			tpm_write8(buf_ptr[count], TPM_DATA_FIFO(locality));
+			count++;
+		}
+
+		/* check for overflow */
+		for (status = 0; (status & TPM_STS_VALID) == 0; )
+			status = tpm_read8(TPM_STS(locality));
+
+		if ((status & TPM_STS_DATA_EXPECT) == 0)
+			return 0;
+	}
+
+	/* write last byte */
+	tpm_write8(buf_ptr[length - 1], TPM_DATA_FIFO(locality));
+	count++;
+
+	/* make sure it stuck */
+	for (status = 0; (status & TPM_STS_VALID) == 0; )
+		status = tpm_read8(TPM_STS(locality));
+
+	if ((status & TPM_STS_DATA_EXPECT) != 0)
+		return 0;
+
+	/* go and do it */
+	tpm_write8(TPM_STS_GO, TPM_STS(locality));
+
+	return (size_t)count;
+}
+
+static u8 tis_init(struct tpm *t)
+{
+	locality = TPM_NO_LOCALITY;
+
+	if (tis_request_locality(0) != 0)
+		return 0;
+
+	t->vendor = tpm_read32(TPM_DID_VID(0));
+	if ((t->vendor & 0xFFFF) == 0xFFFF)
+		return 0;
+
+	return 1;
+}
+
+static u16 tpm_alg_size(u16 alg_id)
+{
+	if (alg_id == TPM_ALG_SHA1)
+		return SHA1_DIGEST_SIZE;
+	else if (alg_id == TPM_ALG_SHA256)
+		return SHA256_DIGEST_SIZE;
+	else if (alg_id == TPM_ALG_SHA512)
+		return SHA512_DIGEST_SIZE;
+
+	return 0;
+}
+
+static int tpm1_pcr_extend(struct tpm *t, u32 pcr, struct tpm_digest *d)
+{
+	struct tpm_buf buf;
+	int ret;
+
+	ret = tpm_buf_init(&buf, TPM_TAG_RQU_COMMAND, TPM_ORD_PCR_EXTEND);
+	if (ret)
+		return ret;
+
+	tpm_buf_append_u32(&buf, pcr);
+	tpm_buf_append(&buf, d->digest, tpm_alg_size(TPM_ALG_SHA1));
+
+	if (tpm_buf_length(&buf) != tis_send(&buf))
+		ret = -EAGAIN;
+
+	return ret;
+}
+
+static int tpm2_extend_pcr(struct tpm *t, u32 pcr, struct tpm_digest *digest)
+{
+	struct tpm_buf buf;
+	u8 auth_area[NULL_AUTH_SIZE] = {0};
+	u32 *handle;
+	int ret;
+
+	ret = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_PCR_EXTEND);
+	if (ret)
+		return ret;
+
+	tpm_buf_append_u32(&buf, pcr);
+
+	/*
+	 * The handle, the first element, is the
+	 * only non-zero value in a NULL auth
+	 */
+	handle = (u32 *)&auth_area;
+	*handle = cpu_to_be32(TPM2_RS_PW);
+
+	tpm_buf_append_u32(&buf, NULL_AUTH_SIZE);
+	tpm_buf_append(&buf, (const unsigned char *)&auth_area,
+                       NULL_AUTH_SIZE);
+
+	tpm_buf_append_u32(&buf, 1);
+
+	tpm_buf_append_u16(&buf, digest->alg_id);
+	tpm_buf_append(&buf, (const unsigned char *)digest->digest,
+		       tpm_alg_size(digest->alg_id));
+
+	if (tpm_buf_length(&buf) != tis_send(&buf))
+		ret = -EAGAIN;
+
+	return ret;
+}
+
+static void find_interface_and_family(struct tpm *t)
+{
+	struct tpm_interface_id intf_id;
+	struct tpm_intf_capability intf_cap;
+
+	/* Sort out whether if it is 1.2 */
+	intf_cap.val = tpm_read32(TPM_INTF_CAPABILITY_0);
+	if ((intf_cap.interface_version == TPM12_TIS_INTF_12) ||
+	    (intf_cap.interface_version == TPM12_TIS_INTF_13)) {
+		t->family = TPM12;
+		t->intf = TPM_TIS;
+		return;
+	}
+
+	/* Assume that it is 2.0 and TIS */
+	t->family = TPM20;
+	t->intf = TPM_TIS;
+
+	/* Check if the interface is CRB */
+	intf_id.val = tpm_read32(TPM_INTERFACE_ID_0);
+	if (intf_id.interface_type == TPM_CRB_INTF_ACTIVE)
+		t->intf = TPM_CRB;
+}
+
+struct tpm *enable_tpm(void)
+{
+	struct tpm *t = &tpm;
+
+	find_interface_and_family(t);
+
+	switch (t->intf) {
+	case TPM_TIS:
+		if (!tis_init(t))
+			return NULL;
+		break;
+	case TPM_CRB:
+		return NULL;
+	}
+
+	return t;
+}
+
+u8 tpm_request_locality(u8 l)
+{
+	return tis_request_locality(l);
+}
+
+int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo,
+		u8 *digest)
+{
+	int ret = -EINVAL;
+
+	if (t->family == TPM12) {
+		struct tpm_digest d;
+
+		if (algo != TPM_ALG_SHA1)
+			return -EINVAL;
+
+		memcpy((void *)d.digest, digest, SHA1_DIGEST_SIZE);
+
+		ret = tpm1_pcr_extend(t, pcr, &d);
+	} else if (t->family == TPM20) {
+		struct tpm_digest *d;
+		u8 buf[MAX_TPM_EXTEND_SIZE];
+
+		d = (struct tpm_digest *) buf;
+		d->alg_id = algo;
+		switch (algo) {
+		case TPM_ALG_SHA1:
+			memcpy(d->digest, digest, SHA1_DIGEST_SIZE);
+			break;
+		case TPM_ALG_SHA256:
+			memcpy(d->digest, digest, SHA256_DIGEST_SIZE);
+			break;
+		case TPM_ALG_SHA512:
+			memcpy(d->digest, digest, SHA512_DIGEST_SIZE);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		ret = tpm2_extend_pcr(t, pcr, d);
+	}
+
+	return ret;
+}
+
+void free_tpm(void)
+{
+	tis_relinquish_locality();
+}
diff --git a/arch/x86/boot/compressed/early_pcr_extend.h b/arch/x86/boot/compressed/early_pcr_extend.h
new file mode 100644
index 000000000000..bcd6d57d8c56
--- /dev/null
+++ b/arch/x86/boot/compressed/early_pcr_extend.h
@@ -0,0 +1,92 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Apertus Solutions, LLC
+ *
+ * Author(s):
+ *      Daniel P. Smith <dpsmith@...rtussolutions.com>
+ *
+ */
+
+#ifndef BOOT_COMPRESSED_EARLY_PCR_EXTEND_H
+#define BOOT_COMPRESSED_EARLY_PCR_EXTEND_H
+
+#define TPM_MMIO_BASE		0xFED40000
+#define TPM_MAX_LOCALITY	4
+#define TPM_NO_LOCALITY		0xFF
+#define TPM_BURST_MIN_DELAY	100 /* 100us */
+#define TPM_ORD_PCR_EXTEND	20
+#define NULL_AUTH_SIZE		9
+#define MAX_TPM_EXTEND_SIZE	68 /* TPM2 SHA512 is the largest */
+
+#define TPM_INTERFACE_ID_0	0x30
+#define TPM_TIS_INTF_ACTIVE	0x00
+#define TPM_CRB_INTF_ACTIVE	0x01
+
+struct tpm_interface_id {
+	union {
+		u32 val;
+		struct {
+			u32 interface_type:4;
+			u32 interface_version:4;
+			u32 cap_locality:1;
+			u32 reserved1:4;
+			u32 cap_tis:1;
+			u32 cap_crb:1;
+			u32 cap_if_res:2;
+			u32 interface_selector:2;
+			u32 intf_sel_lock:1;
+			u32 reserved2:4;
+			u32 reserved3:8;
+		};
+	};
+} __packed;
+
+#define TPM_INTF_CAPABILITY_0	0x14
+#define TPM12_TIS_INTF_12	0x00
+#define TPM12_TIS_INTF_13	0x02
+#define TPM20_TIS_INTF_13	0x03
+
+struct tpm_intf_capability {
+	union {
+		u32 val;
+		struct {
+			u32 data_avail_int_support:1;
+			u32 sts_valid_int_support:1;
+			u32 locality_change_int_support:1;
+			u32 interrupt_level_high:1;
+			u32 interrupt_level_low:1;
+			u32 interrupt_edge_rising:1;
+			u32 interrupt_edge_falling:1;
+			u32 command_ready_int_support:1;
+			u32 burst_count_static:1;
+			u32 data_transfer_size_support:2;
+			u32 reserved1:17;
+			u32 interface_version:3;
+			u32 reserved2:1;
+		};
+	};
+} __packed;
+
+enum tpm_hw_intf {
+	TPM_TIS,
+	TPM_CRB
+};
+
+enum tpm_family {
+	TPM12,
+	TPM20
+};
+
+struct tpm {
+	u32 vendor;
+	enum tpm_family family;
+	enum tpm_hw_intf intf;
+};
+
+extern struct tpm *enable_tpm(void);
+extern u8 tpm_request_locality(u8 l);
+extern int tpm_extend_pcr(struct tpm *t, u32 pcr, u16 algo,
+			  u8 *digest);
+extern void free_tpm(void);
+
+#endif
-- 
2.11.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ