diff --git a/Makefile b/Makefile index 2c3d554..9ceae06 100644 --- a/Makefile +++ b/Makefile @@ -67,9 +67,9 @@ endif LDFLAGS = --hash-style=sysv -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) --build-id=sha1 TARGET = shim.efi MokManager.efi.signed fallback.efi.signed -OBJS = shim.o netboot.o cert.o replacements.o version.o +OBJS = shim.o netboot.o cert.o replacements.o tpm.o version.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer -SOURCES = shim.c shim.h netboot.c include/PeImage.h include/wincert.h include/console.h replacements.c replacements.h version.c version.h +SOURCES = shim.c shim.h netboot.c include/PeImage.h include/wincert.h include/console.h replacements.c replacements.h tpm.c tpm.h version.c version.h MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o MOK_SOURCES = MokManager.c shim.h include/console.h PasswordCrypt.c PasswordCrypt.h crypt_blowfish.c crypt_blowfish.h FALLBACK_OBJS = fallback.o diff --git a/shim.c b/shim.c index b72b4bd..3232a17 100644 --- a/shim.c +++ b/shim.c @@ -41,6 +41,7 @@ #include "netboot.h" #include "shim_cert.h" #include "replacements.h" +#include "tpm.h" #include "ucs2.h" #include "guid.h" @@ -1663,6 +1664,10 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) } } + /* Measure the binary into the TPM */ + tpm_log_event((EFI_PHYSICAL_ADDRESS)data, datasize, 9, + (CHAR8 *)"Second stage bootloader"); + /* * We need to modify the loaded image protocol entry before running * the new binary, so back it up @@ -1732,6 +1737,42 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) return efi_status; } +/* + * Measure some of the MOK variables into the TPM + */ +EFI_STATUS measure_mok() +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + UINT8 *Data = NULL; + UINTN DataSize = 0; + + efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid); + if (efi_status != EFI_SUCCESS) + return efi_status; + + efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)Data, DataSize, 14, + (CHAR8 *)"MokList"); + + FreePool(Data); + + if (efi_status != EFI_SUCCESS) + return efi_status; + + efi_status = get_variable(L"MokSBState", &Data, &DataSize, + shim_lock_guid); + + if (efi_status != EFI_SUCCESS) + return efi_status; + + efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)Data, DataSize, 14, + (CHAR8 *)"MokSBState"); + + FreePool(Data); + + return efi_status; +} + /* * Copy the boot-services only MokList variable to the runtime-accessible * MokListRT variable. It's not marked NV, so the OS can't modify it. @@ -2495,6 +2536,19 @@ efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab) */ debug_hook(); + /* + * Measure the MOK variables + */ + efi_status = measure_mok(); + if (efi_status != EFI_SUCCESS && efi_status != EFI_NOT_FOUND) { + Print(L"Something has gone seriously wrong: %r\n", efi_status); + Print(L"Shim was unable to measure state into the TPM\n"); + systab->BootServices->Stall(5000000); + systab->RuntimeServices->ResetSystem(EfiResetShutdown, + EFI_SECURITY_VIOLATION, + 0, NULL); + } + /* * Check whether the user has configured the system to run in * insecure mode diff --git a/tpm.c b/tpm.c new file mode 100644 index 0000000..71bcf9b --- /dev/null +++ b/tpm.c @@ -0,0 +1,127 @@ +#include +#include +#include + +#include "tpm.h" + +extern UINT8 in_protocol; + +#define perror(fmt, ...) ({ \ + UINTN __perror_ret = 0; \ + if (!in_protocol) \ + __perror_ret = Print((fmt), ##__VA_ARGS__); \ + __perror_ret; \ + }) + + +EFI_GUID tpm_guid = EFI_TPM_GUID; +EFI_GUID tpm2_guid = EFI_TPM2_GUID; + +static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm) +{ + EFI_STATUS status; + TCG_EFI_BOOT_SERVICE_CAPABILITY caps; + UINT32 flags; + EFI_PHYSICAL_ADDRESS eventlog, lastevent; + + caps.Size = (UINT8)sizeof(caps); + status = uefi_call_wrapper(tpm->status_check, 5, tpm, &caps, &flags, + &eventlog, &lastevent); + + if (status != EFI_SUCCESS || caps.TPMDeactivatedFlag + || !caps.TPMPresentFlag) + return FALSE; + + return TRUE; +} + +static BOOLEAN tpm2_present(efi_tpm2_protocol_t *tpm) +{ + EFI_STATUS status; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + EFI_TCG2_BOOT_SERVICE_CAPABILITY_1_0 *caps_1_0; + + caps.Size = (UINT8)sizeof(caps); + + status = uefi_call_wrapper(tpm->get_capability, 2, tpm, &caps); + + if (status != EFI_SUCCESS) + return FALSE; + + if (caps.StructureVersion.Major == 1 && + caps.StructureVersion.Minor == 0) { + caps_1_0 = (EFI_TCG2_BOOT_SERVICE_CAPABILITY_1_0 *)∩︀ + if (caps_1_0->TPMPresentFlag) + return TRUE; + } else { + if (caps.TPMPresentFlag) + return TRUE; + } + + return FALSE; +} + +EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, + const CHAR8 *description) +{ + EFI_STATUS status; + efi_tpm_protocol_t *tpm; + efi_tpm2_protocol_t *tpm2; + + status = LibLocateProtocol(&tpm2_guid, (VOID **)&tpm2); + /* TPM 2.0 */ + if (status == EFI_SUCCESS) { + EFI_TCG2_EVENT *event; + + if (!tpm2_present(tpm2)) + return EFI_SUCCESS; + + event = AllocatePool(sizeof(*event) + strlen(description) + 1); + if (!event) { + perror(L"Unable to allocate event structure\n"); + return EFI_OUT_OF_RESOURCES; + } + + event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER); + event->Header.HeaderVersion = 1; + event->Header.PCRIndex = pcr; + event->Header.EventType = 0x0d; + event->Size = sizeof(*event) - sizeof(event->Event) + strlen(description) + 1; + memcpy(event->Event, description, strlen(description) + 1); + status = uefi_call_wrapper(tpm2->hash_log_extend_event, 5, tpm2, + 0, buf, (UINT64) size, event); + FreePool(event); + return status; + } else { + TCG_PCR_EVENT *event; + UINT32 algorithm, eventnum = 0; + EFI_PHYSICAL_ADDRESS lastevent; + + status = LibLocateProtocol(&tpm_guid, (VOID **)&tpm); + + if (status != EFI_SUCCESS) + return EFI_SUCCESS; + + if (!tpm_present(tpm)) + return EFI_SUCCESS; + + event = AllocatePool(sizeof(*event) + strlen(description) + 1); + + if (!event) { + perror(L"Unable to allocate event structure\n"); + return EFI_OUT_OF_RESOURCES; + } + + event->PCRIndex = pcr; + event->EventType = 0x0d; + event->EventSize = strlen(description) + 1; + algorithm = 0x00000004; + status = uefi_call_wrapper(tpm->log_extend_event, 7, tpm, buf, + (UINT64)size, algorithm, event, + &eventnum, &lastevent); + FreePool(event); + return status; + } + + return EFI_SUCCESS; +} diff --git a/tpm.h b/tpm.h new file mode 100644 index 0000000..2c21b26 --- /dev/null +++ b/tpm.h @@ -0,0 +1,145 @@ +#define EFI_TPM_GUID {0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd }}; +#define EFI_TPM2_GUID {0x607f766c, 0x7455, 0x42be, {0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }}; + +EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, + const CHAR8 *description); + +typedef struct { + uint8_t Major; + uint8_t Minor; + uint8_t RevMajor; + uint8_t RevMinor; +} TCG_VERSION; + +typedef struct _TCG_EFI_BOOT_SERVICE_CAPABILITY { + uint8_t Size; /// Size of this structure. + TCG_VERSION StructureVersion; + TCG_VERSION ProtocolSpecVersion; + uint8_t HashAlgorithmBitmap; /// Hash algorithms . + char TPMPresentFlag; /// 00h = TPM not present. + char TPMDeactivatedFlag; /// 01h = TPM currently deactivated. +} TCG_EFI_BOOT_SERVICE_CAPABILITY; + +typedef struct _TCG_PCR_EVENT { + uint32_t PCRIndex; + uint32_t EventType; + uint8_t digest[20]; + uint32_t EventSize; + uint8_t Event[1]; +} TCG_PCR_EVENT; + +struct efi_tpm_protocol +{ + EFI_STATUS (EFIAPI *status_check) (struct efi_tpm_protocol *this, + TCG_EFI_BOOT_SERVICE_CAPABILITY *ProtocolCapability, + uint32_t *TCGFeatureFlags, + EFI_PHYSICAL_ADDRESS *EventLogLocation, + EFI_PHYSICAL_ADDRESS *EventLogLastEntry); + EFI_STATUS (EFIAPI *hash_all) (struct efi_tpm_protocol *this, + uint8_t *HashData, + uint64_t HashLen, + uint32_t AlgorithmId, + uint64_t *HashedDataLen, + uint8_t **HashedDataResult); + EFI_STATUS (EFIAPI *log_event) (struct efi_tpm_protocol *this, + TCG_PCR_EVENT *TCGLogData, + uint32_t *EventNumber, + uint32_t Flags); + EFI_STATUS (EFIAPI *pass_through_to_tpm) (struct efi_tpm_protocol *this, + uint32_t TpmInputParameterBlockSize, + uint8_t *TpmInputParameterBlock, + uint32_t TpmOutputParameterBlockSize, + uint8_t *TpmOutputParameterBlock); + EFI_STATUS (EFIAPI *log_extend_event) (struct efi_tpm_protocol *this, + EFI_PHYSICAL_ADDRESS HashData, + uint64_t HashDataLen, + uint32_t AlgorithmId, + TCG_PCR_EVENT *TCGLogData, + uint32_t *EventNumber, + EFI_PHYSICAL_ADDRESS *EventLogLastEntry); +}; + +typedef struct efi_tpm_protocol efi_tpm_protocol_t; + +typedef uint32_t EFI_TCG2_EVENT_LOG_BITMAP; +typedef uint32_t EFI_TCG2_EVENT_LOG_FORMAT; +typedef uint32_t EFI_TCG2_EVENT_ALGORITHM_BITMAP; + +typedef struct tdEFI_TCG2_VERSION { + uint8_t Major; + uint8_t Minor; +} __attribute__ ((packed)) EFI_TCG2_VERSION; + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY_1_0 { + uint8_t Size; + EFI_TCG2_VERSION StructureVersion; + EFI_TCG2_VERSION ProtocolVersion; + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + BOOLEAN TPMPresentFlag; + uint16_t MaxCommandSize; + uint16_t MaxResponseSize; + uint32_t ManufacturerID; + uint32_t NumberOfPcrBanks; + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} EFI_TCG2_BOOT_SERVICE_CAPABILITY_1_0; + +typedef struct tdEFI_TCG2_BOOT_SERVICE_CAPABILITY { + uint8_t Size; + EFI_TCG2_VERSION StructureVersion; + EFI_TCG2_VERSION ProtocolVersion; + EFI_TCG2_EVENT_ALGORITHM_BITMAP HashAlgorithmBitmap; + EFI_TCG2_EVENT_LOG_BITMAP SupportedEventLogs; + BOOLEAN TPMPresentFlag; + uint16_t MaxCommandSize; + uint16_t MaxResponseSize; + uint32_t ManufacturerID; + uint32_t NumberOfPcrBanks; + EFI_TCG2_EVENT_ALGORITHM_BITMAP ActivePcrBanks; +} __attribute__ ((packed)) EFI_TCG2_BOOT_SERVICE_CAPABILITY; + +typedef uint32_t TCG_PCRINDEX; +typedef uint32_t TCG_EVENTTYPE; + +typedef struct tdEFI_TCG2_EVENT_HEADER { + uint32_t HeaderSize; + uint16_t HeaderVersion; + TCG_PCRINDEX PCRIndex; + TCG_EVENTTYPE EventType; +} __attribute__ ((packed)) EFI_TCG2_EVENT_HEADER; + +typedef struct tdEFI_TCG2_EVENT { + uint32_t Size; + EFI_TCG2_EVENT_HEADER Header; + uint8_t Event[1]; +} __attribute__ ((packed)) EFI_TCG2_EVENT; + +struct efi_tpm2_protocol +{ + EFI_STATUS (EFIAPI *get_capability) (struct efi_tpm2_protocol *this, + EFI_TCG2_BOOT_SERVICE_CAPABILITY *ProtocolCapability); + EFI_STATUS (EFIAPI *get_event_log) (struct efi_tpm2_protocol *this, + EFI_TCG2_EVENT_LOG_FORMAT EventLogFormat, + EFI_PHYSICAL_ADDRESS *EventLogLocation, + EFI_PHYSICAL_ADDRESS *EventLogLastEntry, + BOOLEAN *EventLogTruncated); + EFI_STATUS (EFIAPI *hash_log_extend_event) (struct efi_tpm2_protocol *this, + uint64_t Flags, + EFI_PHYSICAL_ADDRESS DataToHash, + uint64_t DataToHashLen, + EFI_TCG2_EVENT *EfiTcgEvent); + EFI_STATUS (EFIAPI *submit_command) (struct efi_tpm2_protocol *this, + uint32_t InputParameterBlockSize, + uint8_t *InputParameterBlock, + uint32_t OutputParameterBlockSize, + uint8_t *OutputParameterBlock); + EFI_STATUS (EFIAPI *get_active_pcr_blanks) (struct efi_tpm2_protocol *this, + uint32_t *ActivePcrBanks); + EFI_STATUS (EFIAPI *set_active_pcr_banks) (struct efi_tpm2_protocol *this, + uint32_t ActivePcrBanks); + EFI_STATUS (EFIAPI *get_result_of_set_active_pcr_banks) (struct efi_tpm2_protocol *this, + uint32_t *OperationPresent, + uint32_t *Response); +}; + +typedef struct efi_tpm2_protocol efi_tpm2_protocol_t;