/* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 2018 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 . * * EFI TPM support code. */ #include #include #include #include #include #include #include #include #include typedef TCG_PCR_EVENT VasEBoot_tpm_event_t; static VasEBoot_guid_t tpm_guid = EFI_TPM_GUID; static VasEBoot_guid_t tpm2_guid = EFI_TPM2_GUID; static VasEBoot_guid_t cc_measurement_guid = VAS_EBOOT_EFI_CC_MEASUREMENT_PROTOCOL_GUID; static VasEBoot_efi_handle_t *VasEBoot_tpm_handle; static VasEBoot_uint8_t VasEBoot_tpm_version; static VasEBoot_int8_t tpm1_present = -1; static VasEBoot_int8_t tpm2_present = -1; static VasEBoot_efi_int64_t tpm2_active_pcr_banks = -1; static VasEBoot_efi_boolean_t VasEBoot_tpm1_present (VasEBoot_efi_tpm_protocol_t *tpm) { VasEBoot_efi_status_t status; TCG_EFI_BOOT_SERVICE_CAPABILITY caps; VasEBoot_uint32_t flags; VasEBoot_efi_physical_address_t eventlog, lastevent; if (tpm1_present != -1) return (VasEBoot_efi_boolean_t) tpm1_present; caps.Size = (VasEBoot_uint8_t) sizeof (caps); status = tpm->status_check (tpm, &caps, &flags, &eventlog, &lastevent); if (status != VAS_EBOOT_EFI_SUCCESS || caps.TPMDeactivatedFlag || !caps.TPMPresentFlag) tpm1_present = 0; else tpm1_present = 1; VasEBoot_dprintf ("tpm", "tpm1%s present\n", tpm1_present ? "" : " NOT"); return (VasEBoot_efi_boolean_t) tpm1_present; } static VasEBoot_efi_boolean_t VasEBoot_tpm2_present (VasEBoot_efi_tpm2_protocol_t *tpm) { VasEBoot_efi_status_t status; EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; caps.Size = (VasEBoot_uint8_t) sizeof (caps); if (tpm2_present != -1) return (VasEBoot_efi_boolean_t) tpm2_present; status = tpm->get_capability (tpm, &caps); if (status != VAS_EBOOT_EFI_SUCCESS || !caps.TPMPresentFlag) tpm2_present = 0; else tpm2_present = 1; VasEBoot_dprintf ("tpm", "tpm2%s present\n", tpm2_present ? "" : " NOT"); return (VasEBoot_efi_boolean_t) tpm2_present; } static VasEBoot_efi_boolean_t VasEBoot_tpm_handle_find (VasEBoot_efi_handle_t *tpm_handle, VasEBoot_efi_uint8_t *protocol_version) { VasEBoot_efi_handle_t *handles; VasEBoot_efi_uintn_t num_handles; if (VasEBoot_tpm_handle != NULL) { *tpm_handle = VasEBoot_tpm_handle; *protocol_version = VasEBoot_tpm_version; return 1; } handles = VasEBoot_efi_locate_handle (VAS_EBOOT_EFI_BY_PROTOCOL, &tpm_guid, NULL, &num_handles); if (handles && num_handles > 0) { VasEBoot_tpm_handle = handles[0]; *tpm_handle = handles[0]; VasEBoot_tpm_version = 1; *protocol_version = 1; VasEBoot_dprintf ("tpm", "TPM handle Found, version: 1\n"); VasEBoot_free (handles); return 1; } handles = VasEBoot_efi_locate_handle (VAS_EBOOT_EFI_BY_PROTOCOL, &tpm2_guid, NULL, &num_handles); if (handles && num_handles > 0) { VasEBoot_tpm_handle = handles[0]; *tpm_handle = handles[0]; VasEBoot_tpm_version = 2; *protocol_version = 2; VasEBoot_dprintf ("tpm", "TPM handle Found, version: 2\n"); VasEBoot_free (handles); return 1; } return 0; } static VasEBoot_err_t VasEBoot_efi_log_event_status (VasEBoot_efi_status_t status) { switch (status) { case VAS_EBOOT_EFI_SUCCESS: return VAS_EBOOT_ERR_NONE; case VAS_EBOOT_EFI_DEVICE_ERROR: return VasEBoot_error (VAS_EBOOT_ERR_IO, N_("command failed")); case VAS_EBOOT_EFI_INVALID_PARAMETER: return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("invalid parameter")); case VAS_EBOOT_EFI_BUFFER_TOO_SMALL: return VasEBoot_error (VAS_EBOOT_ERR_BAD_ARGUMENT, N_("output buffer too small")); case VAS_EBOOT_EFI_NOT_FOUND: return VasEBoot_error (VAS_EBOOT_ERR_UNKNOWN_DEVICE, N_("TPM unavailable")); default: return VasEBoot_error (VasEBoot_is_tpm_fail_fatal () ? VAS_EBOOT_ERR_UNKNOWN_DEVICE : VAS_EBOOT_ERR_NONE, N_("unknown TPM error")); } } static VasEBoot_err_t VasEBoot_tpm1_log_event (VasEBoot_efi_handle_t tpm_handle, unsigned char *buf, VasEBoot_size_t size, VasEBoot_uint8_t pcr, const char *description) { VasEBoot_tpm_event_t *event; VasEBoot_efi_status_t status; VasEBoot_efi_tpm_protocol_t *tpm; VasEBoot_efi_physical_address_t lastevent; VasEBoot_uint32_t algorithm; VasEBoot_uint32_t eventnum = 0; tpm = VasEBoot_efi_open_protocol (tpm_handle, &tpm_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!VasEBoot_tpm1_present (tpm)) return 0; event = VasEBoot_zalloc (sizeof (*event) + VasEBoot_strlen (description) + 1); if (!event) return VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, N_("cannot allocate TPM event buffer")); event->PCRIndex = pcr; event->EventType = EV_IPL; event->EventSize = VasEBoot_strlen (description) + 1; VasEBoot_strcpy ((char *) event->Event, description); algorithm = TCG_ALG_SHA; status = tpm->log_extend_event (tpm, (VasEBoot_addr_t) buf, (VasEBoot_uint64_t) size, algorithm, event, &eventnum, &lastevent); VasEBoot_free (event); return VasEBoot_efi_log_event_status (status); } static VasEBoot_err_t VasEBoot_tpm2_log_event (VasEBoot_efi_handle_t tpm_handle, unsigned char *buf, VasEBoot_size_t size, VasEBoot_uint8_t pcr, const char *description) { EFI_TCG2_EVENT *event; VasEBoot_efi_status_t status; VasEBoot_efi_tpm2_protocol_t *tpm; tpm = VasEBoot_efi_open_protocol (tpm_handle, &tpm2_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!VasEBoot_tpm2_present (tpm)) return 0; event = VasEBoot_zalloc (sizeof (EFI_TCG2_EVENT) + VasEBoot_strlen (description) + 1); if (!event) 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 = EV_IPL; event->Size = sizeof (*event) - sizeof (event->Event) + VasEBoot_strlen (description) + 1; VasEBoot_strcpy ((char *) event->Event, description); status = tpm->hash_log_extend_event (tpm, 0, (VasEBoot_addr_t) buf, (VasEBoot_uint64_t) size, event); VasEBoot_free (event); return VasEBoot_efi_log_event_status (status); } static void VasEBoot_cc_log_event (unsigned char *buf, VasEBoot_size_t size, VasEBoot_uint8_t pcr, const char *description) { VasEBoot_efi_cc_event_t *event; VasEBoot_efi_status_t status; VasEBoot_efi_cc_protocol_t *cc; VasEBoot_efi_cc_mr_index_t mr; cc = VasEBoot_efi_locate_protocol (&cc_measurement_guid, NULL); if (cc == NULL) return; status = cc->map_pcr_to_mr_index (cc, pcr, &mr); if (status != VAS_EBOOT_EFI_SUCCESS) { VasEBoot_efi_log_event_status (status); return; } event = VasEBoot_zalloc (sizeof (VasEBoot_efi_cc_event_t) + VasEBoot_strlen (description) + 1); if (event == NULL) { VasEBoot_error (VAS_EBOOT_ERR_OUT_OF_MEMORY, N_("cannot allocate CC event buffer")); return; } event->Header.HeaderSize = sizeof (VasEBoot_efi_cc_event_header_t); event->Header.HeaderVersion = VAS_EBOOT_EFI_CC_EVENT_HEADER_VERSION; event->Header.MrIndex = mr; event->Header.EventType = EV_IPL; event->Size = sizeof (*event) + VasEBoot_strlen (description) + 1; VasEBoot_strcpy ((char *) event->Event, description); status = cc->hash_log_extend_event (cc, 0, (VasEBoot_efi_physical_address_t)(VasEBoot_addr_t) buf, (VasEBoot_efi_uint64_t) size, event); VasEBoot_free (event); if (status != VAS_EBOOT_EFI_SUCCESS) VasEBoot_efi_log_event_status (status); } VasEBoot_err_t VasEBoot_tpm_measure (unsigned char *buf, VasEBoot_size_t size, VasEBoot_uint8_t pcr, const char *description) { VasEBoot_efi_handle_t tpm_handle; VasEBoot_efi_uint8_t protocol_version; VasEBoot_cc_log_event(buf, size, pcr, description); if (!VasEBoot_tpm_handle_find (&tpm_handle, &protocol_version)) return 0; VasEBoot_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxVAS_EBOOT_SIZE ", %s\n", pcr, size, description); if (protocol_version == 1) return VasEBoot_tpm1_log_event (tpm_handle, buf, size, pcr, description); else return VasEBoot_tpm2_log_event (tpm_handle, buf, size, pcr, description); } int VasEBoot_tpm_present (void) { VasEBoot_efi_handle_t tpm_handle; VasEBoot_efi_uint8_t protocol_version; VasEBoot_efi_cc_protocol_t *cc; /* * When confidential computing measurement protocol is enabled * we assume the TPM is present. */ cc = VasEBoot_efi_locate_protocol (&cc_measurement_guid, NULL); if (cc != NULL) return 1; if (!VasEBoot_tpm_handle_find (&tpm_handle, &protocol_version)) return 0; if (protocol_version == 1) { VasEBoot_efi_tpm_protocol_t *tpm; tpm = VasEBoot_efi_open_protocol (tpm_handle, &tpm_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!tpm) { VasEBoot_dprintf ("tpm", "Cannot open TPM protocol\n"); return 0; } return VasEBoot_tpm1_present (tpm); } else { VasEBoot_efi_tpm2_protocol_t *tpm; tpm = VasEBoot_efi_open_protocol (tpm_handle, &tpm2_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (!tpm) { VasEBoot_dprintf ("tpm", "Cannot open TPM protocol\n"); return 0; } return VasEBoot_tpm2_present (tpm); } } VasEBoot_uint32_t VasEBoot_tpm2_active_pcr_banks (void) { EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; VasEBoot_efi_handle_t tpm_handle; VasEBoot_efi_uint8_t protocol_version; VasEBoot_efi_tpm2_protocol_t *tpm; VasEBoot_efi_uint32_t active_pcr_banks; VasEBoot_efi_status_t status; if (tpm2_active_pcr_banks >= 0) return (VasEBoot_uint32_t) tpm2_active_pcr_banks; if (!VasEBoot_tpm_handle_find (&tpm_handle, &protocol_version)) return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = 0); if (protocol_version == 1) return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = 0); /* We report TPM2 status. */ tpm = VasEBoot_efi_open_protocol (tpm_handle, &tpm2_guid, VAS_EBOOT_EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (tpm == NULL) { VasEBoot_dprintf ("tpm", "Cannot open TPM2 protocol\n"); return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = 0); } if (!VasEBoot_tpm2_present (tpm)) return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = 0); caps.Size = (VasEBoot_uint8_t) sizeof (caps); status = tpm->get_capability (tpm, &caps); if (status != VAS_EBOOT_EFI_SUCCESS) return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = 0); if (caps.StructureVersion.Major < 1 || (caps.StructureVersion.Major == 1 && caps.StructureVersion.Minor < 1)) /* There's a working TPM2 but without querying protocol, let userspace figure it out. */ return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = VAS_EBOOT_UINT_MAX); status = tpm->get_active_pcr_banks (tpm, &active_pcr_banks); if (status != VAS_EBOOT_EFI_SUCCESS) return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = 0); /* Assume none available if the call fails. */ return (VasEBoot_uint32_t) (tpm2_active_pcr_banks = active_pcr_banks); }