[PATCH v5 2/2] test: tpm: check malformed capability and OIAP responses are rejected

Shahriyar Jalayeri <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot.general,gmane.comp.boot-loaders.u-boot
Message-ID <[email protected]>
The length of a TPM reply comes from the device and cannot be trusted to
be well-formed. Add DM tests for the malformed replies that the length
checks reject.

For TPM2 GetCapability: one reply too short to hold its header, and one
advertising more data than the caller's buffer holds. The sandbox emulator
gains two test-only capability properties that produce them; the short
reply is caught by the length check in tpm_sendrecv_command(), the
over-long one by the caller's buffer bound.

For the TPM1 OIAP helpers tpm1_load_key2_oiap() and
tpm1_get_pub_key_oiap(): a reply too short to hold the auth trailer that
their length arithmetic subtracts. The emulator answers OIAP with a zeroed
session and both commands with a truncated reply, so the length check
rejects them before the subtraction underflows. These paths need
CONFIG_TPM_AUTH_SESSIONS, enabled here in the sandbox config.

Signed-off-by: Shahriyar Jalayeri <[email protected]>
Acked-by: Ilias Apalodimas <[email protected]>
Reviewed-by: Simon Glass <[email protected]>
---
 configs/sandbox_defconfig      |  1 +
 drivers/tpm/sandbox_common.h   | 10 +++++
 drivers/tpm/tpm2_tis_sandbox.c | 28 +++++++++++++
 drivers/tpm/tpm_tis_sandbox.c  |  9 +++++
 test/dm/tpm.c                  | 89 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 137 insertions(+)

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 79f46317e45..db26b6813de 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -394,6 +394,7 @@ CONFIG_ECDSA=y
 CONFIG_ECDSA_VERIFY=y
 CONFIG_RSASSA_PSS=y
 CONFIG_TPM=y
+CONFIG_TPM_AUTH_SESSIONS=y
 CONFIG_ERRNO_STR=y
 CONFIG_GETOPT=y
 CONFIG_TEST_FDTDEC=y
diff --git a/drivers/tpm/sandbox_common.h b/drivers/tpm/sandbox_common.h
index f423c50ec50..c3f6e15703a 100644
--- a/drivers/tpm/sandbox_common.h
+++ b/drivers/tpm/sandbox_common.h
@@ -14,6 +14,16 @@
  */
 #define TPM_HDR_LEN	10
 
+/*
+ * Sandbox emulator test hooks: a TPM2_GetCapability for these properties makes
+ * the emulated TPM reply with, respectively, a header-only response and one
+ * advertising more property data than a single-property request can hold. They
+ * let tests drive the response parser with a reply shorter or longer than the
+ * data it should carry, as a tampered or faulty TPM on the bus could.
+ */
+#define TPM2_PT_SANDBOX_SHORT_RESPONSE	0x00ffffff
+#define TPM2_PT_SANDBOX_LONG_RESPONSE	0x00fffffe
+
 /* These are the different non-volatile spaces that we emulate */
 enum sandbox_nv_space {
 	NV_SEQ_ENABLE_LOCKING,
diff --git a/drivers/tpm/tpm2_tis_sandbox.c b/drivers/tpm/tpm2_tis_sandbox.c
index 50e308e7116..1fae0fc6ce1 100644
--- a/drivers/tpm/tpm2_tis_sandbox.c
+++ b/drivers/tpm/tpm2_tis_sandbox.c
@@ -542,6 +542,34 @@ static int sandbox_tpm2_xfer(struct udevice *dev, const u8 *sendbuf,
 		property_count = get_unaligned_be32(sent);
 		sent += sizeof(property_count);
 
+		/*
+		 * Test hook: reply with a truncated (header-only) success
+		 * response so the response parser can be exercised against a
+		 * reply too short to hold the data it promises. Honoured for any
+		 * capability so the check can be tested on a path without a
+		 * capability-specific length bound.
+		 */
+		if (property == TPM2_PT_SANDBOX_SHORT_RESPONSE)
+			return sandbox_tpm2_fill_buf(recv, recv_len, tag,
+						     TPM2_RC_SUCCESS);
+
+		/*
+		 * Test hook: reply with a success response advertising more
+		 * property data than a single-property request can hold, to
+		 * exercise the caller-buffer bound in the parser.
+		 */
+		if (capability == TPM2_CAP_TPM_PROPERTIES &&
+		    property == TPM2_PT_SANDBOX_LONG_RESPONSE) {
+			*recv_len = TPM2_HDR_LEN + sizeof(u8) + sizeof(u32) +
+				    sizeof(u32) + TPM2_PROPERTY_NB *
+				    sizeof(struct tpms_tagged_property);
+			put_unaligned_be16(tag, recv);
+			put_unaligned_be32(*recv_len, recv + sizeof(tag));
+			put_unaligned_be32(TPM2_RC_SUCCESS,
+					   recv + sizeof(tag) + sizeof(u32));
+			return 0;
+		}
+
 		switch (capability) {
 		case TPM2_CAP_PCRS:
 			break;
diff --git a/drivers/tpm/tpm_tis_sandbox.c b/drivers/tpm/tpm_tis_sandbox.c
index d7341062b31..1f63e60cbaa 100644
--- a/drivers/tpm/tpm_tis_sandbox.c
+++ b/drivers/tpm/tpm_tis_sandbox.c
@@ -226,6 +226,15 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
 		*recv_len = 12;
 		memset(recvbuf, '\0', *recv_len);
 		break;
+	case 0x0a: /* TPM_ORD_OIAP: hand out a zeroed auth session for tests */
+		*recv_len = TPM_HDR_LEN + sizeof(uint32_t) + 20;
+		memset(recvbuf, '\0', *recv_len);
+		break;
+	case 0x21: /* TPM_ORD_GetPubKey: truncated reply, for the length check */
+	case 0x41: /* TPM_ORD_LoadKey2: truncated reply, for the length check */
+		*recv_len = TPM_HDR_LEN;
+		memset(recvbuf, '\0', *recv_len);
+		break;
 	default:
 		printf("Unknown tpm command %02x\n", code);
 		return -ENOSYS;
diff --git a/test/dm/tpm.c b/test/dm/tpm.c
index 87c5c416daa..7fc58355739 100644
--- a/test/dm/tpm.c
+++ b/test/dm/tpm.c
@@ -6,9 +6,12 @@
 
 #include <dm.h>
 #include <tpm_api.h>
+#include <tpm-v1.h>
+#include <tpm-v2.h>
 #include <dm/test.h>
 #include <test/test.h>
 #include <test/ut.h>
+#include "../../drivers/tpm/sandbox_common.h"
 
 /*
  * get_tpm_version() - Get a TPM of the given version
@@ -197,3 +200,89 @@ static int dm_test_tpm_autostart_reinit(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_tpm_autostart_reinit, UTF_SCAN_FDT);
+
+/*
+ * A TPM sits on a bus a physical attacker can reach, so its responses cannot be
+ * trusted to be well-formed. Check that a reply too short to hold its header is
+ * rejected by tpm_sendrecv_command(), rather than parsed with an underflowed
+ * length.
+ */
+static int dm_test_tpm2_get_capability_short(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 buf[64];
+
+	ut_assertok(get_tpm_version(TPM_V2, &dev));
+	ut_assertok(tpm_auto_start(dev));
+
+	ut_assert(tpm2_get_capability(dev, TPM2_CAP_PCRS,
+				      TPM2_PT_SANDBOX_SHORT_RESPONSE, buf,
+				      sizeof(buf), 1));
+
+	return 0;
+}
+DM_TEST(dm_test_tpm2_get_capability_short, UTF_SCAN_FDT);
+
+/*
+ * Check that a GetCapability reply larger than the caller's buffer is rejected,
+ * rather than copied past the end of it.
+ */
+static int dm_test_tpm2_get_capability_long(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 buf[16];
+
+	ut_assertok(get_tpm_version(TPM_V2, &dev));
+	ut_assertok(tpm_auto_start(dev));
+
+	ut_assert(tpm2_get_capability(dev, TPM2_CAP_TPM_PROPERTIES,
+				      TPM2_PT_SANDBOX_LONG_RESPONSE, buf,
+				      sizeof(buf), 1));
+
+	return 0;
+}
+DM_TEST(dm_test_tpm2_get_capability_long, UTF_SCAN_FDT);
+
+#if CONFIG_IS_ENABLED(TPM_AUTH_SESSIONS)
+/*
+ * A TPM1 OIAP reply carries an auth trailer that is subtracted from a
+ * device-controlled length. Check that a LoadKey2 reply too short to hold that
+ * trailer is rejected, rather than parsed with an underflowed length.
+ */
+static int dm_test_tpm1_load_key2_oiap_short(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 key[8] = {0};
+	u8 auth[20] = {0};
+	u32 handle;
+
+	ut_assertok(get_tpm_version(TPM_V1, &dev));
+	ut_assertok(tpm_init(dev));
+
+	ut_assert(tpm1_load_key2_oiap(dev, 0, key, sizeof(key), auth, &handle));
+
+	return 0;
+}
+DM_TEST(dm_test_tpm1_load_key2_oiap_short, UTF_SCAN_FDT);
+
+/*
+ * The GetPubKey OIAP helper subtracts the same auth trailer from the same
+ * device-controlled length. Check that a reply too short to hold the trailer
+ * is rejected, rather than parsed with an underflowed length.
+ */
+static int dm_test_tpm1_get_pub_key_oiap_short(struct unit_test_state *uts)
+{
+	struct udevice *dev;
+	u8 auth[20] = {0};
+	u8 pubkey[8] = {0};
+	size_t pubkey_len = sizeof(pubkey);
+
+	ut_assertok(get_tpm_version(TPM_V1, &dev));
+	ut_assertok(tpm_init(dev));
+
+	ut_assert(tpm1_get_pub_key_oiap(dev, 0, auth, pubkey, &pubkey_len));
+
+	return 0;
+}
+DM_TEST(dm_test_tpm1_get_pub_key_oiap_short, UTF_SCAN_FDT);
+#endif

-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.