diff options
author | Joel Kitching <kitching@google.com> | 2019-09-23 22:53:49 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-12-03 04:10:42 +0000 |
commit | 50b2d570b298ec4279f588cc22ef789867f03fb6 (patch) | |
tree | 4aa32c4b9b1ab87d3b71034bf66b23dc5719b074 /firmware/lib/secdata_tpm.c | |
parent | d3095bf4def33da46320bd6b25b253850682dced (diff) | |
download | vboot-50b2d570b298ec4279f588cc22ef789867f03fb6.tar.gz |
vboot/secdata: rewrite rollback_index and centralize reads/writes
In current kernel verification code, secdata reads and writes
are spread throughout the code. vboot2's design is to use
vb2_context.secdata_* for storing the state of secdata spaces,
and have the caller (depthcharge) read/save this field when
necessary.
Centralize secdata reads/writes into the functions of
secdata_tpm.c, previously known as rollback_index.c.
Functions which directly read/write to the TPM space are modified
to use vb2_secdata_*_get and vb2_secdata_*_set.
The secure spaces get read/flushed by functions in
vboot_api_kernel.c. These calls and the underlying functions
from secdata_tpm.c will eventually be relocated to depthcharge.
Create a new external function vb2ex_commit_data, which commits
any modified nvdata/secdata. Currently the depthcharge
implementation of this function only writes nvdata, but once
secdata TPM drivers have been migrated from vboot_reference to
depthcharge, it will also commit these data spaces.
This CL also removes the VbExNvStorageRead call from
vb2_kernel_setup, and the data is instead read in depthcharge
CL:1819379, right before calling VbSelectAndLoadKernel.
As such, both the VbExNvStorageRead and VbExNvStorageWrite
functions may be removed.
Finally, create a vb2_secdata_kernel_lock function, which should
be used right before attempting to leave vboot (by booting an OS
or chainloading to another firmware). This should eventually be
exposed as a vb2ex_ API function and relocated to depthcharge.
BUG=b:124141368, chromium:972956, chromium:1006689
TEST=make clean && make runtests
BRANCH=none
Change-Id: Ifbfb21122af0bf85e22a6d3a0d48a1db7f7c25b7
Signed-off-by: Joel Kitching <kitching@google.com>
Cq-Depend: chromium:1819380, chromium:1939168
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1728298
Tested-by: Joel Kitching <kitching@chromium.org>
Reviewed-by: Andrey Pronin <apronin@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Commit-Queue: Joel Kitching <kitching@chromium.org>
(cherry picked from commit adb418310d2e51e2f2a0f22607989fd3f66c4433)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1947715
Reviewed-by: Shelley Chen <shchen@chromium.org>
Commit-Queue: Shelley Chen <shchen@chromium.org>
Tested-by: Shelley Chen <shchen@chromium.org>
Diffstat (limited to 'firmware/lib/secdata_tpm.c')
-rw-r--r-- | firmware/lib/secdata_tpm.c | 263 |
1 files changed, 80 insertions, 183 deletions
diff --git a/firmware/lib/secdata_tpm.c b/firmware/lib/secdata_tpm.c index 6b77ba55..b8f3522c 100644 --- a/firmware/lib/secdata_tpm.c +++ b/firmware/lib/secdata_tpm.c @@ -6,15 +6,12 @@ * stored in the TPM NVRAM. */ +#include "2api.h" #include "2common.h" -#include "2crc8.h" -#include "2nvstorage.h" -#include "2secdata.h" -#include "2sysincludes.h" #include "secdata_tpm.h" #include "tlcl.h" #include "tss_constants.h" -#include "vboot_api.h" +#include "vboot_test.h" #define RETURN_ON_FAILURE(tpm_command) do { \ uint32_t result_; \ @@ -34,9 +31,15 @@ VB2_DEBUG_RAW("\n"); \ } while (0) -uint32_t TPMClearAndReenable(void) +/* Keeps track of whether the kernel space has already been locked or not. */ +int secdata_kernel_locked = 0; + +/** + * Issue a TPM_Clear and reenable/reactivate the TPM. + */ +uint32_t tlcl_clear_and_reenable(void) { - VB2_DEBUG("TPM: clear and re-enable\n"); + VB2_DEBUG("TPM: clear_and_reenable\n"); RETURN_ON_FAILURE(TlclForceClear()); RETURN_ON_FAILURE(TlclSetEnable()); RETURN_ON_FAILURE(TlclSetDeactivated(0)); @@ -44,11 +47,17 @@ uint32_t TPMClearAndReenable(void) return TPM_SUCCESS; } -uint32_t SafeWrite(uint32_t index, const void *data, uint32_t length) +/** + * Like TlclWrite(), but checks for write errors due to hitting the 64-write + * limit and clears the TPM when that happens. This can only happen when the + * TPM is unowned, so it is OK to clear it (and we really have no choice). + * This is not expected to happen frequently, but it could happen. + */ +uint32_t tlcl_safe_write(uint32_t index, const void *data, uint32_t length) { uint32_t result = TlclWrite(index, data, length); if (result == TPM_E_MAXNVWRITES) { - RETURN_ON_FAILURE(TPMClearAndReenable()); + RETURN_ON_FAILURE(tlcl_clear_and_reenable()); return TlclWrite(index, data, length); } else { return result; @@ -56,73 +65,30 @@ uint32_t SafeWrite(uint32_t index, const void *data, uint32_t length) } /* Functions to read and write firmware and kernel spaces. */ -uint32_t ReadSpaceFirmware(RollbackSpaceFirmware *rsf) -{ - uint32_t r; - r = TlclRead(FIRMWARE_NV_INDEX, rsf, sizeof(RollbackSpaceFirmware)); - if (TPM_SUCCESS != r) { - VB2_DEBUG("TPM: read secdata_firmware returned %#x\n", r); - return r; +uint32_t secdata_firmware_write(struct vb2_context *ctx) +{ + if (!(ctx->flags & VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED)) { + VB2_DEBUG("TPM: secdata_firmware unchanged\n"); + return TPM_SUCCESS; } - PRINT_BYTES("TPM: read secdata_firmware", rsf); - - if (rsf->struct_version < ROLLBACK_SPACE_FIRMWARE_VERSION) - return TPM_E_STRUCT_VERSION; - if (rsf->crc8 != vb2_crc8(rsf, offsetof(RollbackSpaceFirmware, crc8))) { - VB2_DEBUG("TPM: bad secdata_firmware CRC\n"); - return TPM_E_CORRUPTED_STATE; + if (!(ctx->flags & VB2_CONTEXT_RECOVERY_MODE)) { + VB2_DEBUG("Error: secdata_firmware modified " + "in non-recovery mode?\n"); + return TPM_E_AREA_LOCKED; } - return TPM_SUCCESS; -} - -uint32_t WriteSpaceFirmware(RollbackSpaceFirmware *rsf) -{ - uint32_t r; - - rsf->crc8 = vb2_crc8(rsf, offsetof(RollbackSpaceFirmware, crc8)); - - PRINT_BYTES("TPM: write secdata", rsf); - r = SafeWrite(FIRMWARE_NV_INDEX, rsf, sizeof(RollbackSpaceFirmware)); - if (TPM_SUCCESS != r) { - VB2_DEBUG("TPM: write secdata_firmware failure\n"); - return r; - } + PRINT_BYTES("TPM: write secdata_firmware", &ctx->secdata_firmware); + RETURN_ON_FAILURE(tlcl_safe_write(FIRMWARE_NV_INDEX, + ctx->secdata_firmware, + VB2_SECDATA_FIRMWARE_SIZE)); + ctx->flags &= ~VB2_CONTEXT_SECDATA_FIRMWARE_CHANGED; return TPM_SUCCESS; } -vb2_error_t SetVirtualDevMode(int val) -{ - RollbackSpaceFirmware rsf; - - VB2_DEBUG("Enabling developer mode...\n"); - - if (TPM_SUCCESS != ReadSpaceFirmware(&rsf)) - return VBERROR_TPM_FIRMWARE_SETUP; - - VB2_DEBUG("TPM: flags were 0x%02x\n", rsf.flags); - if (val) - rsf.flags |= FLAG_VIRTUAL_DEV_MODE_ON; - else - rsf.flags &= ~FLAG_VIRTUAL_DEV_MODE_ON; - /* - * NOTE: This doesn't update the FLAG_LAST_BOOT_DEVELOPER bit. That - * will be done on the next boot. - */ - VB2_DEBUG("TPM: flags are now 0x%02x\n", rsf.flags); - - if (TPM_SUCCESS != WriteSpaceFirmware(&rsf)) - return VBERROR_TPM_SET_BOOT_MODE_STATE; - - VB2_DEBUG("Mode change will take effect on next reboot\n"); - - return VB2_SUCCESS; -} - -uint32_t ReadSpaceKernel(RollbackSpaceKernel *rsk) +uint32_t secdata_kernel_read(struct vb2_context *ctx) { #ifndef TPM2_MODE /* @@ -135,158 +101,89 @@ uint32_t ReadSpaceKernel(RollbackSpaceKernel *rsk) uint32_t perms; RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms)); - - if (perms != TPM_NV_PER_PPWRITE) + if (perms != TPM_NV_PER_PPWRITE) { + VB2_DEBUG("TPM: invalid secdata_kernel permissions: %#x\n", + perms); return TPM_E_CORRUPTED_STATE; -#endif - - uint32_t r; - - r = TlclRead(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel)); - if (TPM_SUCCESS != r) { - VB2_DEBUG("TPM: read secdata_kernel returned %#x\n", r); - return r; } - PRINT_BYTES("TPM: read secdata_kernel", rsk); +#endif - if (rsk->struct_version < ROLLBACK_SPACE_FIRMWARE_VERSION) - return TPM_E_STRUCT_VERSION; + RETURN_ON_FAILURE(TlclRead(KERNEL_NV_INDEX, ctx->secdata_kernel, + VB2_SECDATA_KERNEL_SIZE)); - if (rsk->uid != ROLLBACK_SPACE_KERNEL_UID) - return TPM_E_CORRUPTED_STATE; + PRINT_BYTES("TPM: read secdata_kernel", &ctx->secdata_kernel); - if (rsk->crc8 != vb2_crc8(rsk, offsetof(RollbackSpaceKernel, crc8))) { - VB2_DEBUG("TPM: bad secdata_kernel CRC\n"); + if (vb2api_secdata_kernel_check(ctx)) { + VB2_DEBUG("TPM: secdata_kernel invalid (corrupted?)\n"); return TPM_E_CORRUPTED_STATE; } return TPM_SUCCESS; } -uint32_t WriteSpaceKernel(RollbackSpaceKernel *rsk) +uint32_t secdata_kernel_write(struct vb2_context *ctx) { - uint32_t r; - - rsk->crc8 = vb2_crc8(rsk, offsetof(RollbackSpaceKernel, crc8)); - - PRINT_BYTES("TPM: write secdata_kernel", rsk); - r = SafeWrite(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel)); - if (TPM_SUCCESS != r) { - VB2_DEBUG("TPM: write secdata_kernel failure\n"); - return r; + if (!(ctx->flags & VB2_CONTEXT_SECDATA_KERNEL_CHANGED)) { + VB2_DEBUG("TPM: secdata_kernel unchanged\n"); + return TPM_SUCCESS; } - return TPM_SUCCESS; -} + PRINT_BYTES("TPM: write secdata_kernel", &ctx->secdata_kernel); -uint32_t RollbackKernelRead(uint32_t* version) -{ - RollbackSpaceKernel rsk; - RETURN_ON_FAILURE(ReadSpaceKernel(&rsk)); - memcpy(version, &rsk.kernel_versions, sizeof(*version)); - VB2_DEBUG("TPM: RollbackKernelRead %#x\n", (int)*version); - return TPM_SUCCESS; -} + RETURN_ON_FAILURE(tlcl_safe_write(KERNEL_NV_INDEX, ctx->secdata_kernel, + VB2_SECDATA_KERNEL_SIZE)); -uint32_t RollbackKernelWrite(uint32_t version) -{ - RollbackSpaceKernel rsk; - uint32_t old_version; - RETURN_ON_FAILURE(ReadSpaceKernel(&rsk)); - memcpy(&old_version, &rsk.kernel_versions, sizeof(old_version)); - VB2_DEBUG("TPM: RollbackKernelWrite %#x --> %#x\n", - (int)old_version, (int)version); - memcpy(&rsk.kernel_versions, &version, sizeof(version)); - return WriteSpaceKernel(&rsk); + ctx->flags &= ~VB2_CONTEXT_SECDATA_KERNEL_CHANGED; + return TPM_SUCCESS; } -uint32_t RollbackKernelLock(int recovery_mode) +uint32_t secdata_kernel_lock(struct vb2_context *ctx) { - static int kernel_locked = 0; - uint32_t r; - - if (recovery_mode || kernel_locked) + /* Skip if already locked */ + if (secdata_kernel_locked) { + VB2_DEBUG("TPM: secdata_kernel already locked; skipping\n"); return TPM_SUCCESS; + } - r = TlclLockPhysicalPresence(); - if (TPM_SUCCESS == r) - kernel_locked = 1; + RETURN_ON_FAILURE(TlclLockPhysicalPresence()); - VB2_DEBUG("TPM: lock secdata_kernel returned %#x\n", r); - return r; + VB2_DEBUG("TPM: secdata_kernel locked\n"); + secdata_kernel_locked = 1; + return TPM_SUCCESS; } -uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp) +uint32_t secdata_fwmp_read(struct vb2_context *ctx) { - union { - /* - * Use a union for buf and fwmp, rather than making fwmp a - * pointer to a bare uint8_t[] buffer. This ensures fwmp will - * be aligned if necesssary for the target platform. - */ - uint8_t buf[FWMP_NV_MAX_SIZE]; - struct RollbackSpaceFwmp fwmp; - } u; + vb2_error_t rv; + uint8_t size = VB2_SECDATA_FWMP_MIN_SIZE; uint32_t r; - /* Clear destination in case error or FWMP not present */ - memset(fwmp, 0, sizeof(*fwmp)); - /* Try to read entire 1.0 struct */ - r = TlclRead(FWMP_NV_INDEX, u.buf, sizeof(u.fwmp)); + r = TlclRead(FWMP_NV_INDEX, ctx->secdata_fwmp, size); if (TPM_E_BADINDEX == r) { - /* Missing space is not an error; use defaults */ - VB2_DEBUG("TPM: no FWMP space\n"); + /* Missing space is not an error; tell vboot */ + VB2_DEBUG("TPM: no secdata_fwmp space\n"); + ctx->flags |= VB2_CONTEXT_NO_SECDATA_FWMP; return TPM_SUCCESS; } else if (TPM_SUCCESS != r) { - VB2_DEBUG("TPM: read FWMP returned %#x\n", r); + VB2_DEBUG("TPM: read secdata_fwmp returned %#x\n", r); return r; } - /* - * Struct must be at least big enough for 1.0, but not bigger - * than our buffer size. - */ - if (u.fwmp.struct_size < sizeof(u.fwmp) || - u.fwmp.struct_size > sizeof(u.buf)) { - VB2_DEBUG("TPM: FWMP size invalid: %#x\n", u.fwmp.struct_size); - return TPM_E_STRUCT_SIZE; - } - - /* - * If space is bigger than we expect, re-read so we properly - * compute the CRC. - */ - if (u.fwmp.struct_size > sizeof(u.fwmp)) { - r = TlclRead(FWMP_NV_INDEX, u.buf, u.fwmp.struct_size); - if (TPM_SUCCESS != r) { - VB2_DEBUG("TPM: re-read FWMP returned %#x\n", r); - return r; - } - } + /* Re-read more data if necessary */ + rv = vb2api_secdata_fwmp_check(ctx, &size); + if (rv == VB2_SUCCESS) + return VB2_SUCCESS; - /* Verify CRC */ - if (u.fwmp.crc != vb2_crc8(u.buf + 2, u.fwmp.struct_size - 2)) { - VB2_DEBUG("TPM: bad FWMP CRC\n"); - return TPM_E_CORRUPTED_STATE; - } + if (rv == VB2_ERROR_SECDATA_FWMP_INCOMPLETE) { + RETURN_ON_FAILURE(TlclRead(FWMP_NV_INDEX, ctx->secdata_fwmp, + size)); - /* Verify major version is compatible */ - if ((u.fwmp.struct_version >> 4) != - (ROLLBACK_SPACE_FWMP_VERSION >> 4)) { - VB2_DEBUG("TPM: FWMP major version incompatible\n"); - return TPM_E_STRUCT_VERSION; + /* Check one more time */ + if (vb2api_secdata_fwmp_check(ctx, &size) == VB2_SUCCESS) + return VB2_SUCCESS; } - /* - * Copy to destination. Note that if the space is bigger than - * we expect (due to a minor version change), we only copy the - * part of the FWMP that we know what to do with. - * - * If this were a 1.1+ reader and the source was a 1.0 struct, - * we would need to take care of initializing the extra fields - * added in 1.1+. But that's not an issue yet. - */ - memcpy(fwmp, &u.fwmp, sizeof(*fwmp)); - return TPM_SUCCESS; + VB2_DEBUG("TPM: secdata_fwmp invalid (corrupted?)\n"); + return TPM_E_CORRUPTED_STATE; } |