diff options
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | configure.ac | 23 | ||||
-rw-r--r-- | src/boot/efi/boot.c | 13 | ||||
-rw-r--r-- | src/boot/efi/measure.c | 312 | ||||
-rw-r--r-- | src/boot/efi/measure.h | 21 | ||||
-rw-r--r-- | src/boot/efi/stub.c | 13 |
6 files changed, 386 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am index 3b7cc1e33e..9f051e2954 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2701,6 +2701,7 @@ systemd_boot_headers = \ src/boot/efi/console.h \ src/boot/efi/graphics.h \ src/boot/efi/pefile.h \ + src/boot/efi/measure.h \ src/boot/efi/disk.h systemd_boot_sources = \ @@ -2709,6 +2710,7 @@ systemd_boot_sources = \ src/boot/efi/graphics.c \ src/boot/efi/pefile.c \ src/boot/efi/disk.c \ + src/boot/efi/measure.c \ src/boot/efi/boot.c EXTRA_DIST += $(systemd_boot_sources) $(systemd_boot_headers) @@ -2745,6 +2747,7 @@ stub_headers = \ src/boot/efi/disk.h \ src/boot/efi/graphics.h \ src/boot/efi/splash.h \ + src/boot/efi/measure.h \ src/boot/efi/linux.h stub_sources = \ @@ -2754,6 +2757,7 @@ stub_sources = \ src/boot/efi/graphics.c \ src/boot/efi/splash.c \ src/boot/efi/linux.c \ + src/boot/efi/measure.c \ src/boot/efi/stub.c EXTRA_DIST += \ diff --git a/configure.ac b/configure.ac index 5fd73c59f1..59cc9fd99e 100644 --- a/configure.ac +++ b/configure.ac @@ -1218,6 +1218,28 @@ AS_IF([test "x$enable_gnuefi" != "xno"], [ AM_CONDITIONAL(HAVE_GNUEFI, [test "x$have_gnuefi" = xyes]) # ------------------------------------------------------------------------------ +have_tpm=no +AC_ARG_ENABLE([tpm], AS_HELP_STRING([--enable-tpm],[Enable optional TPM support]), + [case "${enableval}" in + yes) have_tpm=yes ;; + no) have_tpm=no ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-tpm) ;; + esac], + [have_tpm=no]) + +if test "x${have_tpm}" != xno ; then + AC_DEFINE(SD_BOOT_LOG_TPM, 1, [Define if TPM should be used to log events and extend the registers]) +fi + +AC_ARG_WITH(tpm-pcrindex, + AS_HELP_STRING([--with-tpm-pcrindex=<NUM>], + [TPM PCR register number to use]), + [SD_TPM_PCR="$withval"], + [SD_TPM_PCR="8"]) + +AC_DEFINE_UNQUOTED(SD_TPM_PCR, [$SD_TPM_PCR], [TPM PCR register number to use]) + +# ------------------------------------------------------------------------------ have_kdbus=no AC_ARG_ENABLE(kdbus, AS_HELP_STRING([--disable-kdbus], [do not connect to kdbus by default])) if test "x$enable_kdbus" != "xno"; then @@ -1571,6 +1593,7 @@ AC_MSG_RESULT([ dbus: ${have_dbus} nss-myhostname: ${have_myhostname} hwdb: ${enable_hwdb} + tpm: ${have_tpm} kdbus: ${have_kdbus} Python: ${have_python} man pages: ${have_manpages} diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 893980071f..30c1ead1aa 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -22,6 +22,7 @@ #include "linux.h" #include "pefile.h" #include "util.h" +#include "measure.h" #ifndef EFI_OS_INDICATIONS_BOOT_TO_FW_UI #define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001ULL @@ -1644,6 +1645,18 @@ static EFI_STATUS image_start(EFI_HANDLE parent_image, const Config *config, con } loaded_image->LoadOptions = options; loaded_image->LoadOptionsSize = (StrLen(loaded_image->LoadOptions)+1) * sizeof(CHAR16); + +#ifdef SD_BOOT_LOG_TPM + /* Try to log any options to the TPM, escpecially to catch manually edited options */ + err = tpm_log_event(SD_TPM_PCR, + (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, + loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + if (EFI_ERROR(err)) { + Print(L"Unable to add image options measurement: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } +#endif } efivar_set_time_usec(L"LoaderTimeExecUSec", 0); diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c new file mode 100644 index 0000000000..7c016387c1 --- /dev/null +++ b/src/boot/efi/measure.c @@ -0,0 +1,312 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifdef SD_BOOT_LOG_TPM + +#include <efi.h> +#include <efilib.h> +#include "measure.h" + +#define EFI_TCG_PROTOCOL_GUID { 0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd} } + +typedef struct _TCG_VERSION { + UINT8 Major; + UINT8 Minor; + UINT8 RevMajor; + UINT8 RevMinor; +} TCG_VERSION; + +typedef struct _TCG_BOOT_SERVICE_CAPABILITY { + UINT8 Size; + struct _TCG_VERSION StructureVersion; + struct _TCG_VERSION ProtocolSpecVersion; + UINT8 HashAlgorithmBitmap; + BOOLEAN TPMPresentFlag; + BOOLEAN TPMDeactivatedFlag; +} TCG_BOOT_SERVICE_CAPABILITY; + +typedef UINT32 TCG_ALGORITHM_ID; +#define TCG_ALG_SHA 0x00000004 // The SHA1 algorithm + +#define SHA1_DIGEST_SIZE 20 + +typedef struct _TCG_DIGEST { + UINT8 Digest[SHA1_DIGEST_SIZE]; +} TCG_DIGEST; + +#define EV_IPL 13 + +typedef struct _TCG_PCR_EVENT { + UINT32 PCRIndex; + UINT32 EventType; + struct _TCG_DIGEST digest; + UINT32 EventSize; + UINT8 Event[1]; +} TCG_PCR_EVENT; + +INTERFACE_DECL(_EFI_TCG); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_STATUS_CHECK) (IN struct _EFI_TCG * This, + OUT struct _TCG_BOOT_SERVICE_CAPABILITY * ProtocolCapability, + OUT UINT32 * TCGFeatureFlags, + OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_ALL) (IN struct _EFI_TCG * This, + IN UINT8 * HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN OUT UINT64 * HashedDataLen, IN OUT UINT8 ** HashedDataResult); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_LOG_EVENT) (IN struct _EFI_TCG * This, + IN struct _TCG_PCR_EVENT * TCGLogData, + IN OUT UINT32 * EventNumber, IN UINT32 Flags); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_PASS_THROUGH_TO_TPM) (IN struct _EFI_TCG * This, + IN UINT32 TpmInputParameterBlockSize, + IN UINT8 * TpmInputParameterBlock, + IN UINT32 TpmOutputParameterBlockSize, + IN UINT8 * TpmOutputParameterBlock); + +typedef EFI_STATUS(EFIAPI * EFI_TCG_HASH_LOG_EXTEND_EVENT) (IN struct _EFI_TCG * This, + IN EFI_PHYSICAL_ADDRESS HashData, + IN UINT64 HashDataLen, + IN TCG_ALGORITHM_ID AlgorithmId, + IN struct _TCG_PCR_EVENT * TCGLogData, + IN OUT UINT32 * EventNumber, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry); + +typedef struct _EFI_TCG { + EFI_TCG_STATUS_CHECK StatusCheck; + EFI_TCG_HASH_ALL HashAll; + EFI_TCG_LOG_EVENT LogEvent; + EFI_TCG_PASS_THROUGH_TO_TPM PassThroughToTPM; + EFI_TCG_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; +} EFI_TCG; + +#define EFI_TCG2_PROTOCOL_GUID {0x607f766c, 0x7455, 0x42be, { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }} + +typedef struct tdEFI_TCG2_PROTOCOL EFI_TCG2_PROTOCOL; + +typedef struct tdEFI_TCG2_VERSION { + UINT8 Major; + UINT8 Minor; +} EFI_TCG2_VERSION; + +typedef UINT32 EFI_TCG2_EVENT_LOG_BITMAP; +typedef UINT32 EFI_TCG2_EVENT_LOG_FORMAT; +typedef UINT32 EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2 0x00000001 +#define EFI_TCG2_EVENT_LOG_FORMAT_TCG_2 0x00000002 + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + UINT8 Size; + EFI_TCG2_VERSION StructureVersion; + EFI_TCG2_VERSION ProtocolVersion; + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + BOOLEAN TPMPresentFlag; + UINT16 MaxCommandSize; + UINT16 MaxResponseSize; + UINT32 ManufacturerID; + UINT32 NumberOfPCRBanks; + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +#define EFI_TCG2_EVENT_HEADER_VERSION 1 + +typedef struct { + UINT32 HeaderSize; + UINT16 HeaderVersion; + UINT32 PCRIndex; + UINT32 EventType; +} EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + UINT32 Size; + EFI_TCG2_EVENT_HEADER Header; + UINT8 Event[1]; +} EFI_TCG2_EVENT; + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This, + IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_EVENT_LOG) (IN EFI_TCG2_PROTOCOL * This, + IN EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + OUT EFI_PHYSICAL_ADDRESS * EventLogLocation, + OUT EFI_PHYSICAL_ADDRESS * EventLogLastEntry, + OUT BOOLEAN * EventLogTruncated); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_HASH_LOG_EXTEND_EVENT) (IN EFI_TCG2_PROTOCOL * This, + IN UINT64 Flags, + IN EFI_PHYSICAL_ADDRESS DataToHash, + IN UINT64 DataToHashLen, IN EFI_TCG2_EVENT * EfiTcgEvent); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_SUBMIT_COMMAND) (IN EFI_TCG2_PROTOCOL * This, + IN UINT32 InputParameterBlockSize, + IN UINT8 * InputParameterBlock, + IN UINT32 OutputParameterBlockSize, IN UINT8 * OutputParameterBlock); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, OUT UINT32 * ActivePcrBanks); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, IN UINT32 ActivePcrBanks); + +typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS) (IN EFI_TCG2_PROTOCOL * This, + OUT UINT32 * OperationPresent, OUT UINT32 * Response); + +typedef struct tdEFI_TCG2_PROTOCOL { + EFI_TCG2_GET_CAPABILITY GetCapability; + EFI_TCG2_GET_EVENT_LOG GetEventLog; + EFI_TCG2_HASH_LOG_EXTEND_EVENT HashLogExtendEvent; + EFI_TCG2_SUBMIT_COMMAND SubmitCommand; + EFI_TCG2_GET_ACTIVE_PCR_BANKS GetActivePcrBanks; + EFI_TCG2_SET_ACTIVE_PCR_BANKS SetActivePcrBanks; + EFI_TCG2_GET_RESULT_OF_SET_ACTIVE_PCR_BANKS GetResultOfSetActivePcrBanks; +} EFI_TCG2; + + +static EFI_STATUS tpm1_measure_to_pcr_and_event_log(const EFI_TCG *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, + UINTN buffer_size, const CHAR16 *description) { + EFI_STATUS status; + TCG_PCR_EVENT *tcg_event; + UINT32 event_number; + EFI_PHYSICAL_ADDRESS event_log_last; + UINTN desc_len; + + desc_len = (StrLen(description) + 1) * sizeof(CHAR16); + + tcg_event = AllocateZeroPool(desc_len + sizeof(TCG_PCR_EVENT)); + + if (tcg_event == NULL) + return EFI_OUT_OF_RESOURCES; + + tcg_event->EventSize = desc_len; + CopyMem((VOID *) & tcg_event->Event[0], (VOID *) description, desc_len); + + tcg_event->PCRIndex = pcrindex; + tcg_event->EventType = EV_IPL; + + event_number = 1; + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 7, + tcg, buffer, buffer_size, TCG_ALG_SHA, tcg_event, &event_number, &event_log_last); + + if (EFI_ERROR(status)) + return status; + + uefi_call_wrapper(BS->FreePool, 1, tcg_event); + + return EFI_SUCCESS; +} + + +static EFI_STATUS tpm2_measure_to_pcr_and_event_log(const EFI_TCG2 *tcg, UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, + UINT64 buffer_size, const CHAR16 *description) { + EFI_STATUS status; + EFI_TCG2_EVENT *tcg_event; + UINTN desc_len; + + desc_len = StrLen(description) * sizeof(CHAR16); + + tcg_event = AllocateZeroPool(sizeof(*tcg_event) - sizeof(tcg_event->Event) + desc_len + 1); + + if (tcg_event == NULL) + return EFI_OUT_OF_RESOURCES; + + tcg_event->Size = sizeof(EFI_TCG2_EVENT) - sizeof(tcg_event->Event) + desc_len + 1; + tcg_event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); + tcg_event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; + tcg_event->Header.PCRIndex = pcrindex; + tcg_event->Header.EventType = EV_IPL; + + CopyMem((VOID *) tcg_event->Event, (VOID *) description, desc_len); + + status = uefi_call_wrapper(tcg->HashLogExtendEvent, 5, tcg, 0, buffer, buffer_size, tcg_event); + + uefi_call_wrapper(BS->FreePool, 1, tcg_event); + + if (EFI_ERROR(status)) + return status; + + return EFI_SUCCESS; +} + +static EFI_TCG * tcg1_interface_check(void) { + EFI_GUID tpm_guid = EFI_TCG_PROTOCOL_GUID; + EFI_STATUS status; + EFI_TCG *tcg; + TCG_BOOT_SERVICE_CAPABILITY capability; + UINT32 features; + EFI_PHYSICAL_ADDRESS event_log_location; + EFI_PHYSICAL_ADDRESS event_log_last_entry; + + status = LibLocateProtocol(&tpm_guid, (void **) &tcg); + + if (EFI_ERROR(status)) + return NULL; + + capability.Size = (UINT8) sizeof(capability); + status = uefi_call_wrapper(tcg->StatusCheck, 5, tcg, &capability, &features, &event_log_location, &event_log_last_entry); + + if (EFI_ERROR(status)) + return NULL; + + if (capability.TPMDeactivatedFlag) + return NULL; + + if (!capability.TPMPresentFlag) + return NULL; + + return tcg; +} + +static EFI_TCG2 * tcg2_interface_check(void) { + EFI_GUID tpm2_guid = EFI_TCG2_PROTOCOL_GUID; + EFI_STATUS status; + EFI_TCG2 *tcg; + EFI_TCG2_BOOT_SERVICE_CAPABILITY capability; + + status = LibLocateProtocol(&tpm2_guid, (void **) &tcg); + + if (EFI_ERROR(status)) + return NULL; + + capability.Size = (UINT8) sizeof(capability); + status = uefi_call_wrapper(tcg->GetCapability, 2, tcg, &capability); + + if (EFI_ERROR(status)) + return NULL; + + if (!capability.TPMPresentFlag) + return NULL; + + return tcg; +} + +EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { + EFI_TCG *tpm1; + EFI_TCG2 *tpm2; + + tpm2 = tcg2_interface_check(); + if (tpm2) + return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); + + tpm1 = tcg1_interface_check(); + if (tpm1) + return tpm1_measure_to_pcr_and_event_log(tpm1, pcrindex, buffer, buffer_size, description); + + /* No active TPM found, so don't return an error */ + return EFI_SUCCESS; +} + +#endif diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h new file mode 100644 index 0000000000..a2cfe817d0 --- /dev/null +++ b/src/boot/efi/measure.h @@ -0,0 +1,21 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ +#ifndef __SDBOOT_MEASURE_H +#define __SDBOOT_MEASURE_H + +#ifndef SD_TPM_PCR +#define SD_TPM_PCR 8 +#endif + +EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); +#endif diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 9633bc1792..1e250f34f4 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -20,6 +20,7 @@ #include "pefile.h" #include "splash.h" #include "util.h" +#include "measure.h" /* magic string to find in the binary image */ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub " VERSION " ####"; @@ -97,6 +98,18 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { for (i = 0; i < cmdline_len; i++) line[i] = options[i]; cmdline = line; + +#ifdef SD_BOOT_LOG_TPM + /* Try to log any options to the TPM, escpecially manually edited options */ + err = tpm_log_event(SD_TPM_PCR, + (EFI_PHYSICAL_ADDRESS) loaded_image->LoadOptions, + loaded_image->LoadOptionsSize, loaded_image->LoadOptions); + if (EFI_ERROR(err)) { + Print(L"Unable to add image options measurement: %r", err); + uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); + return err; + } +#endif } /* export the device path this image is started from */ |