diff options
author | Steve Langasek <steve.langasek@canonical.com> | 2019-02-09 21:28:06 -0800 |
---|---|---|
committer | Steve Langasek <steve.langasek@canonical.com> | 2019-02-09 21:32:44 -0800 |
commit | ab4c731c1dd379acd3e95971af57401fb0a650a1 (patch) | |
tree | 6a26fb8d0746cbbaa6c2d4b242c73442bcc1df06 /tpm.c | |
parent | 0d63079c7da8e86104ce4bbdae2f6cb8d2ea40c6 (diff) | |
parent | 9c12130f9cd2ae11a9336813dd1f1669c0b64ad0 (diff) | |
download | efi-boot-shim-debian/15+1533136590.3beb971-1.tar.gz efi-boot-shim-debian/15+1533136590.3beb971-1.zip |
* New upstream release.debian/15+1533136590.3beb971-1
- debian/patches/second-stage-path: dropped; the default loader path now
includes an arch suffix.
- debian/patches/sbsigntool-no-pesign: dropped; no longer needed.
* Drop remaining patches that were not being applied.
* Sync packaging from Ubuntu:
- debian/copyright: Update upstream source location.
- debian/control: add a Build-Depends on libelf-dev.
- Enable arm64 build.
- debian/patches/fixup_git.patch: don't run git in clean; we're not
really in a git tree.
- debian/rules, debian/shim.install: use the upstream install target as
intended, and move files to the target directory using dh_install.
- define RELEASE and COMMIT_ID for the snapshot.
- Set ENABLE_HTTPBOOT to enable the HTTP Boot feature.
- Update dh_auto_build/dh_auto_clean/dh_auto_install for new upstream
options: set MAKELEVEL.
- Define an EFI_ARCH variable, and use that for paths to shim. This
makes it possible to build a shim for other architectures than amd64.
- Set EFIDIR=$distro for dh_auto_install; that will let files be installed
in the "right" final directories, and makes boot.csv for us.
- Set ENABLE_SHIM_CERT, to keep using ephemeral self-signed certs built
at compile-time for MokManager and fallback.
- Set ENABLE_SBSIGN, to use sbsign instead of pesign for signing fallback
and MokManager.
Diffstat (limited to 'tpm.c')
-rw-r--r-- | tpm.c | 380 |
1 files changed, 319 insertions, 61 deletions
@@ -1,82 +1,184 @@ #include <efi.h> #include <efilib.h> #include <string.h> +#include <stdint.h> -#include "tpm.h" +#include "shim.h" -extern UINT8 in_protocol; +typedef struct { + CHAR16 *VariableName; + EFI_GUID *VendorGuid; + VOID *Data; + UINTN Size; +} VARIABLE_RECORD; -#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; +UINTN measuredcount = 0; +VARIABLE_RECORD *measureddata = NULL; static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm) { - EFI_STATUS status; + EFI_STATUS efi_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) + efi_status = tpm->status_check(tpm, &caps, &flags, + &eventlog, &lastevent); + if (EFI_ERROR(efi_status) || + caps.TPMDeactivatedFlag || !caps.TPMPresentFlag) return FALSE; return TRUE; } -static BOOLEAN tpm2_present(efi_tpm2_protocol_t *tpm) +static EFI_STATUS tpm2_get_caps(efi_tpm2_protocol_t *tpm, + EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps, + BOOLEAN *old_caps) { - EFI_STATUS status; - EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; - EFI_TCG2_BOOT_SERVICE_CAPABILITY_1_0 *caps_1_0; + EFI_STATUS efi_status; - caps.Size = (UINT8)sizeof(caps); + caps->Size = (UINT8)sizeof(*caps); - status = uefi_call_wrapper(tpm->get_capability, 2, tpm, &caps); + efi_status = tpm->get_capability(tpm, caps); + if (EFI_ERROR(efi_status)) + return efi_status; - if (status != EFI_SUCCESS) - return FALSE; + if (caps->StructureVersion.Major == 1 && + caps->StructureVersion.Minor == 0) + *old_caps = TRUE; + else + *old_caps = 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 EFI_SUCCESS; +} + +static BOOLEAN tpm2_present(EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps, + BOOLEAN old_caps) +{ + TREE_BOOT_SERVICE_CAPABILITY *caps_1_0; + + if (old_caps) { + caps_1_0 = (TREE_BOOT_SERVICE_CAPABILITY *)caps; + if (caps_1_0->TrEEPresentFlag) return TRUE; } + if (caps->TPMPresentFlag) + return TRUE; + return FALSE; } -EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, - const CHAR8 *description) +static inline EFI_TCG2_EVENT_LOG_BITMAP +tpm2_get_supported_logs(efi_tpm2_protocol_t *tpm, + EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps, + BOOLEAN old_caps) +{ + if (old_caps) + return ((TREE_BOOT_SERVICE_CAPABILITY *)caps)->SupportedEventLogs; + + return caps->SupportedEventLogs; +} + +/* + * According to TCG EFI Protocol Specification for TPM 2.0 family, + * all events generated after the invocation of EFI_TCG2_GET_EVENT_LOG + * shall be stored in an instance of an EFI_CONFIGURATION_TABLE aka + * EFI TCG 2.0 final events table. Hence, it is necessary to trigger the + * internal switch through calling get_event_log() in order to allow + * to retrieve the logs from OS runtime. + */ +static EFI_STATUS trigger_tcg2_final_events_table(efi_tpm2_protocol_t *tpm2, + EFI_TCG2_EVENT_LOG_BITMAP supported_logs) { - EFI_STATUS status; + EFI_TCG2_EVENT_LOG_FORMAT log_fmt; + EFI_PHYSICAL_ADDRESS start; + EFI_PHYSICAL_ADDRESS end; + BOOLEAN truncated; + + if (supported_logs & EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) + log_fmt = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; + else + log_fmt = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; + + return tpm2->get_event_log(tpm2, log_fmt, &start, &end, &truncated); +} + +static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm, + efi_tpm2_protocol_t **tpm2, + BOOLEAN *old_caps_p, + EFI_TCG2_BOOT_SERVICE_CAPABILITY *capsp) +{ + EFI_STATUS efi_status; + + *tpm = NULL; + *tpm2 = NULL; + efi_status = LibLocateProtocol(&EFI_TPM2_GUID, (VOID **)tpm2); + /* TPM 2.0 */ + if (!EFI_ERROR(efi_status)) { + BOOLEAN old_caps; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + + efi_status = tpm2_get_caps(*tpm2, &caps, &old_caps); + if (EFI_ERROR(efi_status)) + return efi_status; + + if (tpm2_present(&caps, old_caps)) { + if (old_caps_p) + *old_caps_p = old_caps; + if (capsp) + memcpy(capsp, &caps, sizeof(caps)); + return EFI_SUCCESS; + } + } else { + efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)tpm); + if (EFI_ERROR(efi_status)) + return efi_status; + + if (tpm_present(*tpm)) + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, + UINT8 pcr, const CHAR8 *log, UINTN logsize, + UINT32 type, CHAR8 *hash) +{ + EFI_STATUS efi_status; efi_tpm_protocol_t *tpm; efi_tpm2_protocol_t *tpm2; + BOOLEAN old_caps; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; - status = LibLocateProtocol(&tpm2_guid, (VOID **)&tpm2); - /* TPM 2.0 */ - if (status == EFI_SUCCESS) { + efi_status = tpm_locate_protocol(&tpm, &tpm2, &old_caps, &caps); + if (EFI_ERROR(efi_status)) { +#ifdef REQUIRE_TPM + perror(L"TPM logging failed: %r\n", efi_status); + return efi_status; +#else + if (efi_status != EFI_NOT_FOUND) { + perror(L"TPM logging failed: %r\n", efi_status); + return efi_status; + } +#endif + } else if (tpm2) { EFI_TCG2_EVENT *event; + EFI_TCG2_EVENT_LOG_BITMAP supported_logs; - if (!tpm2_present(tpm2)) - return EFI_SUCCESS; + supported_logs = tpm2_get_supported_logs(tpm2, &caps, old_caps); + + efi_status = trigger_tcg2_final_events_table(tpm2, + supported_logs); + if (EFI_ERROR(efi_status)) { + perror(L"Unable to trigger tcg2 final events table: %r\n", + efi_status); + return efi_status; + } - event = AllocatePool(sizeof(*event) + strlen(description) + 1); + event = AllocatePool(sizeof(*event) + logsize); if (!event) { perror(L"Unable to allocate event structure\n"); return EFI_OUT_OF_RESOURCES; @@ -85,27 +187,37 @@ EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, 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); + event->Header.EventType = type; + event->Size = sizeof(*event) - sizeof(event->Event) + logsize + 1; + CopyMem(event->Event, (VOID *)log, logsize); + if (hash) { + /* TPM 2 systems will generate the appropriate hash + themselves if we pass PE_COFF_IMAGE. In case that + fails we fall back to measuring without it. + */ + efi_status = tpm2->hash_log_extend_event(tpm2, + PE_COFF_IMAGE, buf, (UINT64) size, event); + } + + if (!hash || EFI_ERROR(efi_status)) { + efi_status = tpm2->hash_log_extend_event(tpm2, + 0, buf, (UINT64) size, event); + } FreePool(event); - return status; - } else { + return efi_status; + } else if (tpm) { TCG_PCR_EVENT *event; - UINT32 algorithm, eventnum = 0; + UINT32 eventnum = 0; EFI_PHYSICAL_ADDRESS lastevent; - status = LibLocateProtocol(&tpm_guid, (VOID **)&tpm); - - if (status != EFI_SUCCESS) + efi_status = LibLocateProtocol(&EFI_TPM_GUID, (VOID **)&tpm); + if (EFI_ERROR(efi_status)) return EFI_SUCCESS; if (!tpm_present(tpm)) return EFI_SUCCESS; - event = AllocatePool(sizeof(*event) + strlen(description) + 1); + event = AllocatePool(sizeof(*event) + logsize); if (!event) { perror(L"Unable to allocate event structure\n"); @@ -113,15 +225,161 @@ EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, } 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); + event->EventType = type; + event->EventSize = logsize; + CopyMem(event->Event, (VOID *)log, logsize); + if (hash) { + /* TPM 1.2 devices require us to pass the Authenticode + hash rather than allowing the firmware to attempt + to calculate it */ + CopyMem(event->digest, hash, sizeof(event->digest)); + efi_status = tpm->log_extend_event(tpm, 0, 0, + TPM_ALG_SHA, event, &eventnum, &lastevent); + } else { + efi_status = tpm->log_extend_event(tpm, buf, + (UINT64)size, TPM_ALG_SHA, event, &eventnum, + &lastevent); + } FreePool(event); - return status; + return efi_status; } return EFI_SUCCESS; } + +EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, + const CHAR8 *description) +{ + return tpm_log_event_raw(buf, size, pcr, description, + strlen(description) + 1, 0xd, NULL); +} + +EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 *sha1hash, + UINT8 pcr) +{ + EFI_IMAGE_LOAD_EVENT ImageLoad; + + // All of this is informational and forces us to do more parsing before + // we can generate it, so let's just leave it out for now + ImageLoad.ImageLocationInMemory = 0; + ImageLoad.ImageLengthInMemory = 0; + ImageLoad.ImageLinkTimeAddress = 0; + ImageLoad.LengthOfDevicePath = 0; + + return tpm_log_event_raw(buf, size, pcr, (CHAR8 *)&ImageLoad, + sizeof(ImageLoad), + EV_EFI_BOOT_SERVICES_APPLICATION, sha1hash); +} + +typedef struct { + EFI_GUID VariableName; + UINT64 UnicodeNameLength; + UINT64 VariableDataLength; + CHAR16 UnicodeName[1]; + INT8 VariableData[1]; +} EFI_VARIABLE_DATA_TREE; + +static BOOLEAN tpm_data_measured(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData) +{ + UINTN i; + + for (i=0; i<measuredcount; i++) { + if ((StrCmp (VarName, measureddata[i].VariableName) == 0) && + (CompareGuid (&VendorGuid, measureddata[i].VendorGuid)) && + (VarSize == measureddata[i].Size) && + (CompareMem (VarData, measureddata[i].Data, VarSize) == 0)) { + return TRUE; + } + } + + return FALSE; +} + +static EFI_STATUS tpm_record_data_measurement(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData) +{ + if (measureddata == NULL) { + measureddata = AllocatePool(sizeof(*measureddata)); + } else { + measureddata = ReallocatePool(measureddata, measuredcount * sizeof(*measureddata), + (measuredcount + 1) * sizeof(*measureddata)); + } + + if (measureddata == NULL) + return EFI_OUT_OF_RESOURCES; + + measureddata[measuredcount].VariableName = AllocatePool(StrSize(VarName)); + measureddata[measuredcount].VendorGuid = AllocatePool(sizeof(EFI_GUID)); + measureddata[measuredcount].Data = AllocatePool(VarSize); + + if (measureddata[measuredcount].VariableName == NULL || + measureddata[measuredcount].VendorGuid == NULL || + measureddata[measuredcount].Data == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StrCpy(measureddata[measuredcount].VariableName, VarName); + CopyMem(measureddata[measuredcount].VendorGuid, &VendorGuid, sizeof(EFI_GUID)); + CopyMem(measureddata[measuredcount].Data, VarData, VarSize); + measureddata[measuredcount].Size = VarSize; + measuredcount++; + + return EFI_SUCCESS; +} + +EFI_STATUS tpm_measure_variable(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData) +{ + EFI_STATUS efi_status; + UINTN VarNameLength; + EFI_VARIABLE_DATA_TREE *VarLog; + UINT32 VarLogSize; + + /* Don't measure something that we've already measured */ + if (tpm_data_measured(VarName, VendorGuid, VarSize, VarData)) + return EFI_SUCCESS; + + VarNameLength = StrLen (VarName); + VarLogSize = (UINT32)(sizeof (*VarLog) + + VarNameLength * sizeof (*VarName) + + VarSize - + sizeof (VarLog->UnicodeName) - + sizeof (VarLog->VariableData)); + + VarLog = (EFI_VARIABLE_DATA_TREE *) AllocateZeroPool (VarLogSize); + if (VarLog == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (&VarLog->VariableName, &VendorGuid, + sizeof(VarLog->VariableName)); + VarLog->UnicodeNameLength = VarNameLength; + VarLog->VariableDataLength = VarSize; + CopyMem (VarLog->UnicodeName, VarName, + VarNameLength * sizeof (*VarName)); + CopyMem ((CHAR16 *)VarLog->UnicodeName + VarNameLength, VarData, + VarSize); + + efi_status = tpm_log_event_raw((EFI_PHYSICAL_ADDRESS)(intptr_t)VarLog, + VarLogSize, 7, (CHAR8 *)VarLog, VarLogSize, + EV_EFI_VARIABLE_AUTHORITY, NULL); + + FreePool(VarLog); + + if (EFI_ERROR(efi_status)) + return efi_status; + + return tpm_record_data_measurement(VarName, VendorGuid, VarSize, + VarData); +} + +EFI_STATUS +fallback_should_prefer_reset(void) +{ + EFI_STATUS efi_status; + efi_tpm_protocol_t *tpm; + efi_tpm2_protocol_t *tpm2; + + efi_status = tpm_locate_protocol(&tpm, &tpm2, NULL, NULL); + if (EFI_ERROR(efi_status)) + return EFI_NOT_FOUND; + return EFI_SUCCESS; +} |