[PATCH V4 2/4] tpm: Reserve the TPM final events table

2019-02-22 Thread Matthew Garrett
From: Matthew Garrett 

UEFI systems provide a boot services protocol for obtaining the TPM
event log, but this is unusable after ExitBootServices() is called.
Unfortunately ExitBootServices() itself triggers additional TPM events
that then can't be obtained using this protocol. The platform provides a
mechanism for the OS to obtain these events by recording them to a
separate UEFI configuration table which the OS can then map.

Unfortunately this table isn't self describing in terms of providing its
length, so we need to parse the events inside it to figure out how long
it is. Since the table isn't mapped at this point, we need to extend the
length calculation function to be able to map the event as it goes
along.

Signed-off-by: Matthew Garrett 
---
 drivers/char/tpm/eventlog/tpm2.c |  2 +-
 drivers/firmware/efi/efi.c   |  2 +
 drivers/firmware/efi/tpm.c   | 51 -
 include/linux/efi.h  |  9 +++
 include/linux/tpm_eventlog.h | 94 +---
 5 files changed, 148 insertions(+), 10 deletions(-)

diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c
index dc12e1cbd03a..2cfdf1db4363 100644
--- a/drivers/char/tpm/eventlog/tpm2.c
+++ b/drivers/char/tpm/eventlog/tpm2.c
@@ -40,7 +40,7 @@
 static int calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
struct tcg_pcr_event *event_header)
 {
-   return __calc_tpm2_event_size(event, event_header);
+   return __calc_tpm2_event_size(event, event_header, false);
 }
 
 static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
index 55b77c576c42..6b11c41e0575 100644
--- a/drivers/firmware/efi/efi.c
+++ b/drivers/firmware/efi/efi.c
@@ -53,6 +53,7 @@ struct efi __read_mostly efi = {
.mem_attr_table = EFI_INVALID_TABLE_ADDR,
.rng_seed   = EFI_INVALID_TABLE_ADDR,
.tpm_log= EFI_INVALID_TABLE_ADDR,
+   .tpm_final_log  = EFI_INVALID_TABLE_ADDR,
.mem_reserve= EFI_INVALID_TABLE_ADDR,
 };
 EXPORT_SYMBOL(efi);
@@ -485,6 +486,7 @@ static __initdata efi_config_table_type_t common_tables[] = 
{
{EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", _attr_table},
{LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", _seed},
{LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", _log},
+   {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", _final_log},
{LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", _reserve},
{NULL_GUID, NULL, NULL},
 };
diff --git a/drivers/firmware/efi/tpm.c b/drivers/firmware/efi/tpm.c
index 0cbeb3d46b18..2ccaa6661aaf 100644
--- a/drivers/firmware/efi/tpm.c
+++ b/drivers/firmware/efi/tpm.c
@@ -10,24 +10,50 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 
+int efi_tpm_final_log_size;
+EXPORT_SYMBOL(efi_tpm_final_log_size);
+
+static int tpm2_calc_event_log_size(void *data, int count, void *size_info)
+{
+   struct tcg_pcr_event2_head *header;
+   int event_size, size = 0;
+
+   while (count > 0) {
+   header = data + size;
+   event_size = __calc_tpm2_event_size(header, size_info, true);
+   if (event_size == 0)
+   return -1;
+   size += event_size;
+   }
+
+   return size;
+}
+
 /*
  * Reserve the memory associated with the TPM Event Log configuration table.
  */
 int __init efi_tpm_eventlog_init(void)
 {
struct linux_efi_tpm_eventlog *log_tbl;
+   struct efi_tcg2_final_events_table *final_tbl;
unsigned int tbl_size;
 
-   if (efi.tpm_log == EFI_INVALID_TABLE_ADDR)
+   if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) {
+   /*
+* We can't calculate the size of the final events without the
+* first entry in the TPM log, so bail here.
+*/
return 0;
+   }
 
log_tbl = early_memremap(efi.tpm_log, sizeof(*log_tbl));
if (!log_tbl) {
pr_err("Failed to map TPM Event Log table @ 0x%lx\n",
-   efi.tpm_log);
+  efi.tpm_log);
efi.tpm_log = EFI_INVALID_TABLE_ADDR;
return -ENOMEM;
}
@@ -35,6 +61,27 @@ int __init efi_tpm_eventlog_init(void)
tbl_size = sizeof(*log_tbl) + log_tbl->size;
memblock_reserve(efi.tpm_log, tbl_size);
early_memunmap(log_tbl, sizeof(*log_tbl));
+
+   if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR)
+   return 0;
+
+   final_tbl = early_memremap(efi.tpm_final_log, sizeof(*final_tbl));
+
+   if (!final_tbl) {
+   pr_err("Failed to map TPM Final Event Log table @ 0x%lx\n",
+  efi.tpm_final_log);
+   efi.tpm_final_log = EFI_INVALID_TABLE_ADDR;
+   return -ENOMEM;
+   }
+
+   tbl_size = tpm2_calc_event_log_size(final_tbl->events,
+  

[PATCH V4 3/4] tpm: Append the final event log to the TPM event log

2019-02-22 Thread Matthew Garrett
From: Matthew Garrett 

Any events that are logged after GetEventsLog() is called are logged to
the EFI Final Events table. These events are defined as being in the
crypto agile log format, so we can just append them directly to the
existing log if it's in the same format. In theory we can also construct
old-style SHA1 log entries for devices that only return logs in that
format, but EDK2 doesn't generate the final event log in that case so
it doesn't seem worth it at the moment.

Signed-off-by: Matthew Garrett 
---
 drivers/char/tpm/eventlog/efi.c | 50 -
 1 file changed, 43 insertions(+), 7 deletions(-)

diff --git a/drivers/char/tpm/eventlog/efi.c b/drivers/char/tpm/eventlog/efi.c
index 3e673ab22cb4..9179cf6bdee9 100644
--- a/drivers/char/tpm/eventlog/efi.c
+++ b/drivers/char/tpm/eventlog/efi.c
@@ -21,10 +21,13 @@
 int tpm_read_log_efi(struct tpm_chip *chip)
 {
 
+   struct efi_tcg2_final_events_table *final_tbl = NULL;
struct linux_efi_tpm_eventlog *log_tbl;
struct tpm_bios_log *log;
u32 log_size;
u8 tpm_log_version;
+   void *tmp;
+   int ret;
 
if (!(chip->flags & TPM_CHIP_FLAG_TPM2))
return -ENODEV;
@@ -52,15 +55,48 @@ int tpm_read_log_efi(struct tpm_chip *chip)
 
/* malloc EventLog space */
log->bios_event_log = kmemdup(log_tbl->log, log_size, GFP_KERNEL);
-   if (!log->bios_event_log)
-   goto err_memunmap;
-   log->bios_event_log_end = log->bios_event_log + log_size;
+   if (!log->bios_event_log) {
+   ret = -ENOMEM;
+   goto out;
+   }
 
+   log->bios_event_log_end = log->bios_event_log + log_size;
tpm_log_version = log_tbl->version;
-   memunmap(log_tbl);
-   return tpm_log_version;
 
-err_memunmap:
+   ret = tpm_log_version;
+
+   if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR ||
+   efi_tpm_final_log_size == 0 ||
+   tpm_log_version != EFI_TCG2_EVENT_LOG_FORMAT_TCG_2)
+   goto out;
+
+   final_tbl = memremap(efi.tpm_final_log,
+sizeof(*final_tbl) + efi_tpm_final_log_size,
+MEMREMAP_WB);
+   if (!final_tbl) {
+   pr_err("Could not map UEFI TPM final log\n");
+   kfree(log->bios_event_log);
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   tmp = krealloc(log->bios_event_log,
+  log_size + efi_tpm_final_log_size,
+  GFP_KERNEL);
+   if (!tmp) {
+   kfree(log->bios_event_log);
+   ret = -ENOMEM;
+   goto out;
+   }
+
+   log->bios_event_log = tmp;
+   memcpy((void *)log->bios_event_log + log_size,
+  final_tbl->events, efi_tpm_final_log_size);
+   log->bios_event_log_end = log->bios_event_log +
+   log_size + efi_tpm_final_log_size;
+
+out:
+   memunmap(final_tbl);
memunmap(log_tbl);
-   return -ENOMEM;
+   return ret;
 }
-- 
2.21.0.rc0.258.g878e2cd30e-goog



[PATCH V4 0/4] Add support for TCG2 event logs on EFI systems

2019-02-22 Thread Matthew Garrett
This patchset adds support for obtaining the TCG2 format event log on
EFI systems, along with support for copying up the final event log to
capture events that occur after the primary log is obtained. V4 is
identical to previous versions, except for tpm_read_log_efi() in patch 3
being reworked to reduce nesting.




[PATCH V4 4/4] efi: Attempt to get the TCG2 event log in the boot stub

2019-02-22 Thread Matthew Garrett
From: Matthew Garrett 

Right now we only attempt to obtain the SHA1-only event log. The
protocol also supports a crypto agile log format, which contains digests
for all algorithms in use. Attempt to obtain this first, and fall back
to obtaining the older format if the system doesn't support it. This is
lightly complicated by the event sizes being variable (as we don't know
in advance which algorithms are in use), and the interface giving us
back a pointer to the start of the final entry rather than a pointer to
the end of the log - as a result, we need to parse the final entry to
figure out its length in order to know how much data to copy up to the
OS.

Signed-off-by: Matthew Garrett 
---
 drivers/firmware/efi/libstub/tpm.c | 50 --
 1 file changed, 33 insertions(+), 17 deletions(-)

diff --git a/drivers/firmware/efi/libstub/tpm.c 
b/drivers/firmware/efi/libstub/tpm.c
index a90b0b8fc69a..523cd07c551c 100644
--- a/drivers/firmware/efi/libstub/tpm.c
+++ b/drivers/firmware/efi/libstub/tpm.c
@@ -59,7 +59,7 @@ void efi_enable_reset_attack_mitigation(efi_system_table_t 
*sys_table_arg)
 
 #endif
 
-static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
+void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
 {
efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID;
@@ -69,6 +69,7 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t 
*sys_table_arg)
unsigned long first_entry_addr, last_entry_addr;
size_t log_size, last_entry_size;
efi_bool_t truncated;
+   int version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
void *tcg2_protocol = NULL;
 
status = efi_call_early(locate_protocol, _guid, NULL,
@@ -76,14 +77,20 @@ static void 
efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
if (status != EFI_SUCCESS)
return;
 
-   status = efi_call_proto(efi_tcg2_protocol, get_event_log, tcg2_protocol,
-   EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2,
-   _location, _last_entry, );
-   if (status != EFI_SUCCESS)
-   return;
+   status = efi_call_proto(efi_tcg2_protocol, get_event_log,
+   tcg2_protocol, version, _location,
+   _last_entry, );
+
+   if (status != EFI_SUCCESS || !log_location) {
+   version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
+   status = efi_call_proto(efi_tcg2_protocol, get_event_log,
+   tcg2_protocol, version, _location,
+   _last_entry, );
+   if (status != EFI_SUCCESS || !log_location)
+   return;
+
+   }
 
-   if (!log_location)
-   return;
first_entry_addr = (unsigned long) log_location;
 
/*
@@ -98,8 +105,23 @@ static void 
efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
 * We need to calculate its size to deduce the full size of
 * the logs.
 */
-   last_entry_size = sizeof(struct tcpa_event) +
-   ((struct tcpa_event *) last_entry_addr)->event_size;
+   if (version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) {
+   /*
+* The TCG2 log format has variable length entries,
+* and the information to decode the hash algorithms
+* back into a size is contained in the first entry -
+* pass a pointer to the final entry (to calculate its
+* size) and the first entry (so we know how long each
+* digest is)
+*/
+   last_entry_size =
+   __calc_tpm2_event_size((void *)last_entry_addr,
+  (void *)log_location,
+  false);
+   } else {
+   last_entry_size = sizeof(struct tcpa_event) +
+  ((struct tcpa_event *) last_entry_addr)->event_size;
+   }
log_size = log_last_entry - log_location + last_entry_size;
}
 
@@ -116,7 +138,7 @@ static void 
efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
 
memset(log_tbl, 0, sizeof(*log_tbl) + log_size);
log_tbl->size = log_size;
-   log_tbl->version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
+   log_tbl->version = version;
memcpy(log_tbl->log, (void *) first_entry_addr, log_size);
 
status = efi_call_early(install_configuration_table,
@@ -128,9 +150,3 @@ static void 
efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg)
 err_free:
efi_call_early(free_pool, log_tbl);
 }
-
-void 

[PATCH V4 1/4] tpm: Abstract crypto agile event size calculations

2019-02-22 Thread Matthew Garrett
From: Matthew Garrett 

We need to calculate the size of crypto agile events in multiple
locations, including in the EFI boot stub. The easiest way to do this is
to put it in a header file as an inline and leave a wrapper to ensure we
don't end up with multiple copies of it embedded in the existing code.

Signed-off-by: Matthew Garrett 
---
 drivers/char/tpm/eventlog/tpm2.c | 47 +-
 include/linux/tpm_eventlog.h | 68 
 2 files changed, 69 insertions(+), 46 deletions(-)

diff --git a/drivers/char/tpm/eventlog/tpm2.c b/drivers/char/tpm/eventlog/tpm2.c
index d8b77133a83a..dc12e1cbd03a 100644
--- a/drivers/char/tpm/eventlog/tpm2.c
+++ b/drivers/char/tpm/eventlog/tpm2.c
@@ -40,52 +40,7 @@
 static int calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
struct tcg_pcr_event *event_header)
 {
-   struct tcg_efi_specid_event_head *efispecid;
-   struct tcg_event_field *event_field;
-   void *marker;
-   void *marker_start;
-   u32 halg_size;
-   size_t size;
-   u16 halg;
-   int i;
-   int j;
-
-   marker = event;
-   marker_start = marker;
-   marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
-   + sizeof(event->count);
-
-   efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
-
-   /* Check if event is malformed. */
-   if (event->count > efispecid->num_algs)
-   return 0;
-
-   for (i = 0; i < event->count; i++) {
-   halg_size = sizeof(event->digests[i].alg_id);
-   memcpy(, marker, halg_size);
-   marker = marker + halg_size;
-   for (j = 0; j < efispecid->num_algs; j++) {
-   if (halg == efispecid->digest_sizes[j].alg_id) {
-   marker +=
-   efispecid->digest_sizes[j].digest_size;
-   break;
-   }
-   }
-   /* Algorithm without known length. Such event is unparseable. */
-   if (j == efispecid->num_algs)
-   return 0;
-   }
-
-   event_field = (struct tcg_event_field *)marker;
-   marker = marker + sizeof(event_field->event_size)
-   + event_field->event_size;
-   size = marker - marker_start;
-
-   if ((event->event_type == 0) && (event_field->event_size == 0))
-   return 0;
-
-   return size;
+   return __calc_tpm2_event_size(event, event_header);
 }
 
 static void *tpm2_bios_measurements_start(struct seq_file *m, loff_t *pos)
diff --git a/include/linux/tpm_eventlog.h b/include/linux/tpm_eventlog.h
index f47342361e87..09c19d506b69 100644
--- a/include/linux/tpm_eventlog.h
+++ b/include/linux/tpm_eventlog.h
@@ -117,4 +117,72 @@ struct tcg_pcr_event2_head {
struct tpm2_digest digests[];
 } __packed;
 
+/**
+ * __calc_tpm2_event_size - calculate the size of a TPM2 event log entry
+ * @event:Pointer to the event whose size should be calculated
+ * @event_header: Pointer to the initial event containing the digest lengths
+ *
+ * The TPM2 event log format can contain multiple digests corresponding to
+ * separate PCR banks, and also contains a variable length of the data that
+ * was measured. This requires knowledge of how long each digest type is,
+ * and this information is contained within the first event in the log.
+ *
+ * We calculate the length by examining the number of events, and then looking
+ * at each event in turn to determine how much space is used for events in
+ * total. Once we've done this we know the offset of the data length field,
+ * and can calculate the total size of the event.
+ *
+ * Return: size of the event on success, <0 on failure
+ */
+
+static inline int __calc_tpm2_event_size(struct tcg_pcr_event2_head *event,
+struct tcg_pcr_event *event_header)
+{
+   struct tcg_efi_specid_event_head *efispecid;
+   struct tcg_event_field *event_field;
+   void *marker;
+   void *marker_start;
+   u32 halg_size;
+   size_t size;
+   u16 halg;
+   int i;
+   int j;
+
+   marker = event;
+   marker_start = marker;
+   marker = marker + sizeof(event->pcr_idx) + sizeof(event->event_type)
+   + sizeof(event->count);
+
+   efispecid = (struct tcg_efi_specid_event_head *)event_header->event;
+
+   /* Check if event is malformed. */
+   if (event->count > efispecid->num_algs)
+   return 0;
+
+   for (i = 0; i < event->count; i++) {
+   halg_size = sizeof(event->digests[i].alg_id);
+   memcpy(, marker, halg_size);
+   marker = marker + halg_size;
+   for (j = 0; j < efispecid->num_algs; j++) {
+   if (halg == efispecid->digest_sizes[j].alg_id) {
+   marker +=
+