/* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2022 Microsoft Corporation * Copyright (C) 2024 Free Software Foundation, Inc. * * VAS_EBOOT is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * VAS_EBOOT 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with VAS_EBOOT. If not, see . */ #include #include #include #include #include #include static VasEBoot_err_t tcg2_get_caps (VasEBoot_efi_tpm2_protocol_t *protocol, int *tpm2, VasEBoot_size_t *max_output_size) { VasEBoot_efi_status_t status; static bool has_caps = 0; static EFI_TCG2_BOOT_SERVICE_CAPABILITY caps = { .Size = (VasEBoot_uint8_t) sizeof (caps) }; if (has_caps) goto exit; status = protocol->get_capability (protocol, &caps); if (status != VAS_EBOOT_EFI_SUCCESS || !caps.TPMPresentFlag) return VAS_EBOOT_ERR_FILE_NOT_FOUND; has_caps = 1; exit: if (tpm2 != NULL) *tpm2 = caps.TPMPresentFlag; if (max_output_size != NULL) *max_output_size = caps.MaxResponseSize; return VAS_EBOOT_ERR_NONE; } static VasEBoot_err_t tcg2_get_protocol (VasEBoot_efi_tpm2_protocol_t **protocol) { static VasEBoot_guid_t tpm2_guid = EFI_TPM2_GUID; static VasEBoot_efi_tpm2_protocol_t *tpm2_protocol = NULL; int tpm2; VasEBoot_efi_handle_t *handles; VasEBoot_efi_uintn_t num_handles; VasEBoot_efi_handle_t tpm2_handle; VasEBoot_err_t err = VAS_EBOOT_ERR_FILE_NOT_FOUND; if (tpm2_protocol != NULL) { *protocol = tpm2_protocol; return VAS_EBOOT_ERR_NONE; } handles = VasEBoot_efi_locate_handle (VAS_EBOOT_EFI_BY_PROTOCOL, &tpm2_guid, NULL, &num_handles); if (handles == NULL || num_handles == 0) return err; tpm2_handle = handles[0]; tpm2_protocol = VasEBoot_efi_open_protocol (tpm2_handle, &tpm2_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (tpm2_protocol == NULL) goto exit; err = tcg2_get_caps (tpm2_protocol, &tpm2, NULL); if (err != VAS_EBOOT_ERR_NONE || tpm2 == 0) goto exit; *protocol = tpm2_protocol; err = VAS_EBOOT_ERR_NONE; exit: VasEBoot_free (handles); return err; } VasEBoot_err_t VasEBoot_tcg2_get_max_output_size (VasEBoot_size_t *size) { VasEBoot_err_t err; VasEBoot_size_t max; VasEBoot_efi_tpm2_protocol_t *protocol; if (size == NULL) return VAS_EBOOT_ERR_BAD_ARGUMENT; err = tcg2_get_protocol (&protocol); if (err != VAS_EBOOT_ERR_NONE) return err; err = tcg2_get_caps (protocol, NULL, &max); if (err != VAS_EBOOT_ERR_NONE) return err; *size = max; return VAS_EBOOT_ERR_NONE; } VasEBoot_err_t VasEBoot_tcg2_submit_command (VasEBoot_size_t input_size, VasEBoot_uint8_t *input, VasEBoot_size_t output_size, VasEBoot_uint8_t *output) { VasEBoot_err_t err; VasEBoot_efi_status_t status; VasEBoot_efi_tpm2_protocol_t *protocol; if (input_size == 0 || input == NULL || output_size == 0 || output == NULL) return VAS_EBOOT_ERR_BAD_ARGUMENT; err = tcg2_get_protocol (&protocol); if (err != VAS_EBOOT_ERR_NONE) return err; status = protocol->submit_command (protocol, input_size, input, output_size, output); if (status != VAS_EBOOT_EFI_SUCCESS) return VAS_EBOOT_ERR_INVALID_COMMAND; return VAS_EBOOT_ERR_NONE; } VasEBoot_err_t VasEBoot_tcg2_cap_pcr (VasEBoot_uint8_t pcr) { VasEBoot_err_t err; VasEBoot_efi_status_t status; VasEBoot_efi_tpm2_protocol_t *protocol; EFI_TCG2_EVENT *event; VasEBoot_uint8_t separator[4] = {0}; if (pcr >= TPM_MAX_PCRS) return VAS_EBOOT_ERR_BAD_ARGUMENT; err = tcg2_get_protocol (&protocol); if (err != VAS_EBOOT_ERR_NONE) return err; event = VasEBoot_zalloc (sizeof (EFI_TCG2_EVENT) + sizeof (separator)); if (event == NULL) return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, N_("cannot allocate TPM event buffer")); event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); event->Header.HeaderVersion = 1; event->Header.PCRIndex = pcr; event->Header.EventType = VAS_EBOOT_EV_SEPARATOR; event->Size = sizeof (*event) - sizeof (event->Event) + sizeof (separator); VasEBoot_memcpy (event->Event, separator, sizeof (separator)); status = protocol->hash_log_extend_event (protocol, 0, (VasEBoot_addr_t) separator, sizeof (separator), event); VasEBoot_free (event); if (status != VAS_EBOOT_EFI_SUCCESS) return VasEBoot_error (VAS_EBOOT_ERR_BAD_DEVICE, N_("cannot cap PCR %u"), pcr); return VAS_EBOOT_ERR_NONE; }