/
githubmirror
/
shim
Обзор
Документация
Войти
/
githubmirror
/
shim
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tpm.c
471 строка
12 KB
Jason Montleon
Make string function argument types match gnu-efi's requirements.
24 июн 2026, 17:32
24 июн 2026, 17:32
90c470c
Код
Авторство
О чём код?
// SPDX-License-Identifier: BSD-2-Clause-Patent #include "shim.h" typedef struct { CHAR16 *VariableName; EFI_GUID *VendorGuid; VOID *Data; UINTN Size; } VARIABLE_RECORD; UINTN measuredcount = 0; VARIABLE_RECORD *measureddata = NULL; static BOOLEAN tpm_defective = FALSE; static BOOLEAN log_full_already_warned = FALSE; static BOOLEAN tpm_present(efi_tpm_protocol_t *tpm) { EFI_STATUS efi_status; TCG_EFI_BOOT_SERVICE_CAPABILITY caps; UINT32 flags; EFI_PHYSICAL_ADDRESS eventlog, lastevent; if (tpm_defective) return FALSE; caps.Size = (UINT8)sizeof(caps); efi_status = tpm->status_check(tpm, &caps, &flags, &eventlog, &lastevent); if (EFI_ERROR(efi_status) || caps.TPMDeactivatedFlag || !caps.TPMPresentFlag) return FALSE; return TRUE; } static EFI_STATUS tpm2_get_caps(efi_tpm2_protocol_t *tpm, EFI_TCG2_BOOT_SERVICE_CAPABILITY *caps, BOOLEAN *old_caps) { EFI_STATUS efi_status; caps->Size = (UINT8)sizeof(*caps); efi_status = tpm->get_capability(tpm, caps); if (EFI_ERROR(efi_status)) return efi_status; if (caps->StructureVersion.Major == 1 && caps->StructureVersion.Minor == 0) *old_caps = TRUE; else *old_caps = FALSE; 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; } 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 void warn_first_log_full(void) { if (!log_full_already_warned) { perror(L"TPM extend operation occurred, but the event could" " not be written to one or more event logs. Applications" " reliant on a valid event log will not function.\n"); log_full_already_warned = TRUE; } } static EFI_STATUS cc_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr, const CHAR8 *log, UINTN logsize, UINT32 type, BOOLEAN is_pe_image) { EFI_STATUS efi_status; EFI_CC_EVENT *event; efi_cc_protocol_t *cc; EFI_CC_MR_INDEX mr; uint64_t flags = is_pe_image ? EFI_CC_FLAG_PE_COFF_IMAGE : 0; efi_status = LibLocateProtocol(&EFI_CC_MEASUREMENT_PROTOCOL_GUID, (VOID **)&cc); if (EFI_ERROR(efi_status) || !cc) return EFI_SUCCESS; efi_status = cc->map_pcr_to_mr_index(cc, pcr, &mr); if (EFI_ERROR(efi_status)) return EFI_NOT_FOUND; UINTN event_size = sizeof(*event) - sizeof(event->Event) + logsize; event = AllocatePool(event_size); if (!event) { perror(L"Unable to allocate event structure\n"); return EFI_OUT_OF_RESOURCES; } event->Header.HeaderSize = sizeof(EFI_CC_EVENT_HEADER); event->Header.HeaderVersion = EFI_CC_EVENT_HEADER_VERSION; event->Header.MrIndex = mr; event->Header.EventType = type; event->Size = event_size; CopyMem(event->Event, (VOID *)log, logsize); efi_status = cc->hash_log_extend_event(cc, flags, buf, (UINT64)size, event); /* Per spec: The extend operation occurred, but the event could * not be written to one or more event logs. We can still safely * boot in this case, but also show a warning to let the user know. */ if (efi_status == EFI_VOLUME_FULL) { warn_first_log_full(); efi_status = EFI_SUCCESS; } FreePool(event); return efi_status; } 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; /* CC guest like TDX or SEV will measure the buffer and log the event, extend the result into a specific CC MR like TCG's PCR. It could coexists with TCG's TPM 1.2 and TPM 2. */ efi_status = cc_log_event_raw(buf, size, pcr, log, logsize, type, (hash != NULL)); if (EFI_ERROR(efi_status)) return efi_status; 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; UINTN event_size = sizeof(*event) - sizeof(event->Event) + logsize; event = AllocatePool(event_size); 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 = type; event->Size = event_size; 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 (efi_status == EFI_VOLUME_FULL) { warn_first_log_full(); efi_status = EFI_SUCCESS; } } if (!hash || EFI_ERROR(efi_status)) { efi_status = tpm2->hash_log_extend_event(tpm2, 0, buf, (UINT64) size, event); if (efi_status == EFI_VOLUME_FULL) { warn_first_log_full(); efi_status = EFI_SUCCESS; } } FreePool(event); return efi_status; } else if (tpm) { TCG_PCR_EVENT *event; UINT32 eventnum = 0; EFI_PHYSICAL_ADDRESS lastevent; 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) + logsize); if (!event) { perror(L"Unable to allocate event structure\n"); return EFI_OUT_OF_RESOURCES; } event->PCRIndex = pcr; 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); if (efi_status == EFI_VOLUME_FULL) { warn_first_log_full(); efi_status = EFI_SUCCESS; } } else { efi_status = tpm->log_extend_event(tpm, buf, (UINT64)size, TPM_ALG_SHA, event, &eventnum, &lastevent); if (efi_status == EFI_VOLUME_FULL) { warn_first_log_full(); efi_status = EFI_SUCCESS; } } if (efi_status == EFI_UNSUPPORTED) { perror(L"Could not write TPM event: %r. Considering " "the TPM as defective.\n", efi_status); tpm_defective = TRUE; efi_status = EFI_SUCCESS; } FreePool(event); 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((char *)description) + 1, EV_IPL, NULL); } EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size, EFI_PHYSICAL_ADDRESS addr, EFI_DEVICE_PATH *path, UINT8 *sha1hash, UINT8 pcr) { EFI_IMAGE_LOAD_EVENT *ImageLoad = NULL; EFI_STATUS efi_status; UINTN path_size = 0; if (path) path_size = DevicePathSize(path); ImageLoad = AllocateZeroPool(sizeof(*ImageLoad) + path_size); if (!ImageLoad) { perror(L"Unable to allocate image load event structure\n"); return EFI_OUT_OF_RESOURCES; } ImageLoad->ImageLocationInMemory = buf; ImageLoad->ImageLengthInMemory = size; ImageLoad->ImageLinkTimeAddress = addr; if (path_size > 0) { CopyMem(ImageLoad->DevicePath, path, path_size); ImageLoad->LengthOfDevicePath = path_size; } efi_status = tpm_log_event_raw(buf, size, pcr, (CHAR8 *)ImageLoad, sizeof(*ImageLoad) + path_size, EV_EFI_BOOT_SERVICES_APPLICATION, (CHAR8 *)sha1hash); FreePool(ImageLoad); return efi_status; } typedef struct { EFI_GUID VariableName; UINT64 UnicodeNameLength; UINT64 VariableDataLength; CHAR16 UnicodeName[1]; INT8 VariableData[1]; } __attribute__ ((packed)) 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(measuredcount * sizeof(*measureddata), (measuredcount + 1) * sizeof(*measureddata), 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; } #ifdef SHIM_UNIT_TEST static void DESTRUCTOR tpm_clean_up_measurements(void) { for (UINTN i = 0; i < measuredcount; i++) { VARIABLE_RECORD *vr = &measureddata[i]; if (vr->VariableName) FreePool(vr->VariableName); if (vr->VendorGuid) FreePool(vr->VendorGuid); if (vr->Data) FreePool(vr->Data); } if (measureddata) FreePool(measureddata); measuredcount = 0; measureddata = NULL; } #endif