diff mbox series

[v4,32/33] tpm2: Implement TPM 2.0 menu item to activate and deactivte PCR banks

Message ID 20191211202728.127996-33-stefanb@linux.vnet.ibm.com
State Superseded
Headers show
Series Add vTPM support to SLOF | expand

Commit Message

Stefan Berger Dec. 11, 2019, 8:27 p.m. UTC
Implement a TPM 2.0 menu item that allows a user to toggle the activation
of PCR banks of the TPM 2.0. After successful activation we shut down the
TPM 2.0 and reset the machine.

Background:

A TPM 2.0 may have multiple PCR banks, such as for SHA1, SHA256, SHA384,
SHA512, and SM3-256. One or multiple of those banks may be active (by
factory for example) and modifying the set of active PCR banks is only
possible while in the firmware since it requires platform authorization.
Platform authorization is not possible for a user when in the OS since
the firmware generates a random password for the platform authorization
before booting the system and it throws that password away.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/helpers.h        |   1 +
 lib/libtpm/tcgbios.c     | 225 +++++++++++++++++++++++++++++++++++++++
 lib/libtpm/tcgbios_int.h |  17 +++
 slof/helpers.c           |   5 +
 4 files changed, 248 insertions(+)
diff mbox series

Patch

diff --git a/include/helpers.h b/include/helpers.h
index c6a2ccd..e4aa8fa 100644
--- a/include/helpers.h
+++ b/include/helpers.h
@@ -44,6 +44,7 @@  extern int SLOF_get_property(const char *node, const char *propname,
                              char **addr, int *len);
 extern unsigned long SLOF_get_vtpm_unit(void);
 extern int SLOF_get_keystroke(void);
+extern void SLOF_reset(void);
 
 #define offset_of(type, member) ((long) &((type *)0)->member)
 #define container_of(ptr, type, member) ({                      \
diff --git a/lib/libtpm/tcgbios.c b/lib/libtpm/tcgbios.c
index d0a4c9c..eef42ba 100644
--- a/lib/libtpm/tcgbios.c
+++ b/lib/libtpm/tcgbios.c
@@ -139,23 +139,36 @@  struct tpm_log_entry {
 
 static const struct hash_parameters {
 	uint16_t hashalg;
+	uint8_t  hashalg_flag;
 	uint8_t  hash_buffersize;
+	const char *name;
 } hash_parameters[] = {
 	{
 		.hashalg = TPM2_ALG_SHA1,
+		.hashalg_flag = TPM2_ALG_SHA1_FLAG,
 		.hash_buffersize = SHA1_BUFSIZE,
+		.name = "SHA1",
 	}, {
 		.hashalg = TPM2_ALG_SHA256,
+		.hashalg_flag = TPM2_ALG_SHA256_FLAG,
 		.hash_buffersize = SHA256_BUFSIZE,
+		.name = "SHA256",
 	}, {
 		.hashalg = TPM2_ALG_SHA384,
+		.hashalg_flag = TPM2_ALG_SHA384_FLAG,
 		.hash_buffersize = SHA384_BUFSIZE,
+		.name = "SHA384",
+
 	}, {
 		.hashalg = TPM2_ALG_SHA512,
+		.hashalg_flag = TPM2_ALG_SHA512_FLAG,
 		.hash_buffersize = SHA512_BUFSIZE,
+		.name = "SHA512",
 	}, {
 		.hashalg = TPM2_ALG_SM3_256,
+		.hashalg_flag = TPM2_ALG_SM3_256_FLAG,
 		.hash_buffersize = SM3_256_BUFSIZE,
+		.name = "SM3-256",
 	}
 };
 
@@ -171,6 +184,42 @@  tpm20_get_hash_buffersize(uint16_t hashAlg)
 	return -1;
 }
 
+static uint8_t
+tpm20_hashalg_to_flag(uint16_t hashAlg)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg == hashAlg)
+			return hash_parameters[i].hashalg_flag;
+	}
+	return 0;
+}
+
+static uint16_t
+tpm20_hashalg_flag_to_hashalg(uint8_t hashalg_flag)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg_flag == hashalg_flag)
+			return hash_parameters[i].hashalg;
+	}
+	return 0;
+}
+
+static const char *
+tpm20_hashalg_flag_to_name(uint8_t hashalg_flag)
+{
+	unsigned i;
+
+	for (i = 0; i < ARRAY_SIZE(hash_parameters); i++) {
+		if (hash_parameters[i].hashalg_flag == hashalg_flag)
+			return hash_parameters[i].name;
+	}
+	return NULL;
+}
+
 /*
  * Build the TPM2 tpm2_digest_values data structure from the given hash.
  * Follow the PCR bank configuration of the TPM and write the same hash
@@ -381,6 +430,116 @@  tpm20_get_pcrbanks(void)
 	return ret;
 }
 
+static int
+tpm20_get_suppt_pcrbanks(uint8_t *suppt_pcrbanks, uint8_t *active_pcrbanks)
+{
+	*suppt_pcrbanks = 0;
+	*active_pcrbanks = 0;
+
+	if (!tpm20_pcr_selection)
+		return -1;
+
+	struct tpms_pcr_selection *sel = tpm20_pcr_selection->selections;
+	void *end = (void*)tpm20_pcr_selection + tpm20_pcr_selection_size;
+
+	while (1) {
+		uint8_t sizeOfSelect = sel->sizeOfSelect;
+		void *nsel = (void*)sel + sizeof(*sel) + sizeOfSelect;
+		if (nsel > end)
+			return 0;
+
+		uint16_t hashalg = be16_to_cpu(sel->hashAlg);
+		uint8_t hashalg_flag = tpm20_hashalg_to_flag(hashalg);
+
+		*suppt_pcrbanks |= hashalg_flag;
+
+		unsigned i;
+		for (i = 0; i < sizeOfSelect; i++) {
+			if (sel->pcrSelect[i]) {
+				*active_pcrbanks |= hashalg_flag;
+				break;
+			}
+		}
+
+		sel = nsel;
+	}
+}
+
+static int
+tpm20_set_pcrbanks(uint32_t active_banks)
+{
+	struct tpm2_req_pcr_allocate trpa = {
+		.hdr.tag = cpu_to_be16(TPM2_ST_SESSIONS),
+		.hdr.ordinal = cpu_to_be32(TPM2_CC_PCR_Allocate),
+		.authhandle = cpu_to_be32(TPM2_RH_PLATFORM),
+		.authblocksize = cpu_to_be32(sizeof(trpa.authblock)),
+		.authblock = {
+			.handle = cpu_to_be32(TPM2_RS_PW),
+			.noncesize = cpu_to_be16(0),
+			.contsession = TPM2_YES,
+			.pwdsize = cpu_to_be16(0),
+		},
+	};
+	struct tpms_pcr_selection3 {
+		uint16_t hashAlg;
+		uint8_t sizeOfSelect;
+		uint8_t pcrSelect[3];
+	} tps[ARRAY_SIZE(trpa.tpms_pcr_selections)];
+	int i = 0;
+	uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+	uint8_t dontcare, suppt_banks;
+
+	tpm20_get_suppt_pcrbanks(&suppt_banks, &dontcare);
+
+	while (hashalg_flag) {
+		if ((hashalg_flag & suppt_banks)) {
+			uint16_t hashalg = tpm20_hashalg_flag_to_hashalg(hashalg_flag);
+
+			if (hashalg) {
+				uint8_t mask = 0;
+
+				tps[i].hashAlg = cpu_to_be16(hashalg);
+				tps[i].sizeOfSelect = 3;
+
+				if (active_banks & hashalg_flag)
+					mask = 0xff;
+
+				tps[i].pcrSelect[0] = mask;
+				tps[i].pcrSelect[1] = mask;
+				tps[i].pcrSelect[2] = mask;
+				i++;
+			}
+		}
+		hashalg_flag <<= 1;
+	}
+
+	trpa.count = cpu_to_be32(i);
+	memcpy(trpa.tpms_pcr_selections, tps, i * sizeof(tps[0]));
+	trpa.hdr.totlen = cpu_to_be32(offset_of(struct tpm2_req_pcr_allocate,
+						tpms_pcr_selections) +
+				      i * sizeof(tps[0]));
+
+	struct tpm_rsp_header rsp;
+	uint32_t resp_length = sizeof(rsp);
+
+	int ret = tpmhw_transmit(0, &trpa.hdr, &rsp, &resp_length,
+				 TPM_DURATION_TYPE_SHORT);
+	ret = ret ? -1 : be32_to_cpu(rsp.errcode);
+
+	return ret;
+}
+
+static int tpm20_activate_pcrbanks(uint32_t active_banks)
+{
+	int ret = tpm20_set_pcrbanks(active_banks);
+	if (!ret)
+		ret = tpm_simple_cmd(0, TPM2_CC_Shutdown,
+				     2, TPM2_SU_CLEAR, TPM_DURATION_TYPE_SHORT);
+	if (!ret)
+		SLOF_reset();
+	return ret;
+}
+
 static int tpm12_get_capability(uint32_t cap, uint32_t subcap,
 			  struct tpm_rsp_header *rsp, uint32_t rsize)
 {
@@ -1671,6 +1830,67 @@  uint32_t tpm_get_tpm_version(void)
 	return TPM_version;
 }
 
+static int tpm20_menu_change_active_pcrbanks(void)
+{
+	uint8_t active_banks, suppt_banks;
+
+	tpm20_get_suppt_pcrbanks(&suppt_banks, &active_banks);
+
+	uint8_t activate_banks = active_banks;
+
+	while (1) {
+		uint8_t hashalg_flag = TPM2_ALG_SHA1_FLAG;
+		uint8_t i = 0;
+
+		printf("\nToggle active PCR banks by pressing number key\n\n");
+
+		while (hashalg_flag) {
+			uint8_t flag = hashalg_flag & suppt_banks;
+			const char *hashname = tpm20_hashalg_flag_to_name(flag);
+
+			i++;
+			if (hashname) {
+				printf("  %d: %s", i, hashname);
+				if (activate_banks & hashalg_flag)
+					printf(" (enabled)");
+				printf("\n");
+			}
+
+			hashalg_flag <<= 1;
+		}
+		printf("\n"
+		       "ESC: return to previous menu without changes\n");
+		if (activate_banks)
+			printf("A  : activate selection\n");
+
+		uint8_t flagnum;
+		int show = 0;
+		while (!show) {
+			int key_code = SLOF_get_keystroke();
+
+			switch (key_code) {
+			case ~0:
+				continue;
+			case 27: /* ESC */
+				printf("\n");
+				return -1;
+			case '1' ... '5': /* keys 1 .. 5 */
+				flagnum = key_code - '0';
+				if (flagnum > i)
+					continue;
+				if (suppt_banks & (1 << (flagnum - 1))) {
+					activate_banks ^= 1 << (flagnum - 1);
+					show = 1;
+				}
+				break;
+			case 'a': /* a */
+				if (activate_banks)
+					tpm20_activate_pcrbanks(activate_banks);
+			}
+		}
+	}
+}
+
 void tpm20_menu(void)
 {
 	int key_code;
@@ -1679,6 +1899,7 @@  void tpm20_menu(void)
 
 	for (;;) {
 		printf("1. Clear TPM\n");
+		printf("2. Change active PCR banks\n");
 
 		printf("\nIf not change is desired or if this menu was reached by "
 		       "mistake, press ESC to\ncontinue the boot.\n");
@@ -1696,6 +1917,10 @@  void tpm20_menu(void)
 			case '1':
 				msgCode = TPM_PPI_OP_CLEAR;
 				break;
+			case '2':
+				tpm20_menu_change_active_pcrbanks();
+				waitkey = 0;
+				continue;
 			default:
 				continue;
 			}
diff --git a/lib/libtpm/tcgbios_int.h b/lib/libtpm/tcgbios_int.h
index ce4d9c2..67b0854 100644
--- a/lib/libtpm/tcgbios_int.h
+++ b/lib/libtpm/tcgbios_int.h
@@ -270,6 +270,12 @@  struct tpm_rsp_getcap_buffersize {
 #define TPM2_ALG_SHA512             0x000d
 #define TPM2_ALG_SM3_256            0x0012
 
+#define TPM2_ALG_SHA1_FLAG          (1 << 0)
+#define TPM2_ALG_SHA256_FLAG        (1 << 1)
+#define TPM2_ALG_SHA384_FLAG        (1 << 2)
+#define TPM2_ALG_SHA512_FLAG        (1 << 3)
+#define TPM2_ALG_SM3_256_FLAG       (1 << 4)
+
 /* TPM 2 command tags */
 #define TPM2_ST_NO_SESSIONS         0x8001
 #define TPM2_ST_SESSIONS            0x8002
@@ -279,8 +285,10 @@  struct tpm_rsp_getcap_buffersize {
 #define TPM2_CC_Clear               0x126
 #define TPM2_CC_ClearControl        0x127
 #define TPM2_CC_HierarchyChangeAuth 0x129
+#define TPM2_CC_PCR_Allocate        0x12b
 #define TPM2_CC_SelfTest            0x143
 #define TPM2_CC_Startup             0x144
+#define TPM2_CC_Shutdown            0x145
 #define TPM2_CC_StirRandom          0x146
 #define TPM2_CC_GetCapability       0x17a
 #define TPM2_CC_GetRandom           0x17b
@@ -373,6 +381,15 @@  struct tpm2_res_getcapability {
 	uint8_t data[0]; /* capability dependent data */
 } __attribute__((packed));
 
+struct tpm2_req_pcr_allocate {
+	struct tpm_req_header hdr;
+	uint32_t authhandle;
+	uint32_t authblocksize;
+	struct tpm2_authblock authblock;
+	uint32_t count;
+	uint8_t tpms_pcr_selections[4];
+} __attribute__((packed));
+
 struct tpms_pcr_selection {
 	uint16_t hashAlg;
 	uint8_t sizeOfSelect;
diff --git a/slof/helpers.c b/slof/helpers.c
index a651c17..354d690 100644
--- a/slof/helpers.c
+++ b/slof/helpers.c
@@ -236,3 +236,8 @@  int SLOF_get_keystroke(void)
 	forth_eval("key");
 	return forth_pop();
 }
+
+void SLOF_reset(void)
+{
+	forth_eval("reset-all");
+}