123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- *
- * Functions for querying, manipulating and locking rollback indices
- * stored in the TPM NVRAM.
- */
- #include "2sysincludes.h"
- #include "2common.h"
- #include "2crc8.h"
- #include "sysincludes.h"
- #include "rollback_index.h"
- #include "tlcl.h"
- #include "tss_constants.h"
- #include "utility.h"
- #include "vboot_api.h"
- #ifndef offsetof
- #define offsetof(A,B) __builtin_offsetof(A,B)
- #endif
- /*
- * Provide protoypes for functions not in the header file. These prototypes
- * fix -Wmissing-prototypes warnings.
- */
- uint32_t ReadSpaceFirmware(RollbackSpaceFirmware *rsf);
- uint32_t WriteSpaceFirmware(RollbackSpaceFirmware *rsf);
- uint32_t ReadSpaceKernel(RollbackSpaceKernel *rsk);
- uint32_t WriteSpaceKernel(RollbackSpaceKernel *rsk);
- #ifdef FOR_TEST
- /*
- * Compiling for unit test, so we need the real implementations of
- * rollback functions. The unit test mocks the underlying tlcl
- * functions, so this is ok to run on the host.
- */
- #undef CHROMEOS_ENVIRONMENT
- #undef DISABLE_ROLLBACK_TPM
- #endif
- #define RETURN_ON_FAILURE(tpm_command) do { \
- uint32_t result_; \
- if ((result_ = (tpm_command)) != TPM_SUCCESS) { \
- VB2_DEBUG("Rollback: %08x returned by " #tpm_command \
- "\n", (int)result_); \
- return result_; \
- } \
- } while (0)
- uint32_t TPMClearAndReenable(void)
- {
- VB2_DEBUG("TPM: Clear and re-enable\n");
- RETURN_ON_FAILURE(TlclForceClear());
- RETURN_ON_FAILURE(TlclSetEnable());
- RETURN_ON_FAILURE(TlclSetDeactivated(0));
- return TPM_SUCCESS;
- }
- uint32_t SafeWrite(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 TlclWrite(index, data, length);
- } else {
- return result;
- }
- }
- /* Functions to read and write firmware and kernel spaces. */
- uint32_t ReadSpaceFirmware(RollbackSpaceFirmware *rsf)
- {
- uint32_t r;
- int attempts = 3;
- while (attempts--) {
- r = TlclRead(FIRMWARE_NV_INDEX, rsf,
- sizeof(RollbackSpaceFirmware));
- if (r != TPM_SUCCESS)
- return r;
- /*
- * No CRC in this version, so we'll create one when we write
- * it. Note that we're marking this as version 2, not
- * ROLLBACK_SPACE_FIRMWARE_VERSION, because version 2 just
- * added the CRC. Later versions will need to set default
- * values for any extra fields explicitly (probably here).
- */
- if (rsf->struct_version < 2) {
- /* Danger Will Robinson! Danger! */
- rsf->struct_version = 2;
- return TPM_SUCCESS;
- }
- /*
- * If the CRC is good, we're done. If it's bad, try a couple
- * more times to see if it gets better before we give up. It
- * could just be noise.
- */
- if (rsf->crc8 == vb2_crc8(rsf,
- offsetof(RollbackSpaceFirmware, crc8)))
- return TPM_SUCCESS;
- VB2_DEBUG("TPM: bad CRC\n");
- }
- VB2_DEBUG("TPM: too many bad CRCs, giving up\n");
- return TPM_E_CORRUPTED_STATE;
- }
- uint32_t WriteSpaceFirmware(RollbackSpaceFirmware *rsf)
- {
- RollbackSpaceFirmware rsf2;
- uint32_t r;
- int attempts = 3;
- /* All writes should use struct_version 2 or greater. */
- if (rsf->struct_version < 2)
- rsf->struct_version = 2;
- rsf->crc8 = vb2_crc8(rsf, offsetof(RollbackSpaceFirmware, crc8));
- while (attempts--) {
- r = SafeWrite(FIRMWARE_NV_INDEX, rsf,
- sizeof(RollbackSpaceFirmware));
- /* Can't write, not gonna try again */
- if (r != TPM_SUCCESS)
- return r;
- /* Read it back to be sure it got the right values. */
- r = ReadSpaceFirmware(&rsf2); /* This checks the CRC */
- if (r == TPM_SUCCESS)
- return r;
- VB2_DEBUG("TPM: bad CRC\n");
- /* Try writing it again. Maybe it was garbled on the way out. */
- }
- VB2_DEBUG("TPM: too many bad CRCs, giving up\n");
- return TPM_E_CORRUPTED_STATE;
- }
- uint32_t SetVirtualDevMode(int val)
- {
- RollbackSpaceFirmware rsf;
- VB2_DEBUG("TPM: Entering");
- 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("TPM: Leaving\n");
- return VBERROR_SUCCESS;
- }
- uint32_t ReadSpaceKernel(RollbackSpaceKernel *rsk)
- {
- uint32_t r;
- int attempts = 3;
- while (attempts--) {
- r = TlclRead(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel));
- if (r != TPM_SUCCESS)
- return r;
- /*
- * No CRC in this version, so we'll create one when we write
- * it. Note that we're marking this as version 2, not
- * ROLLBACK_SPACE_KERNEL_VERSION, because version 2 just added
- * the CRC. Later versions will need to set default values for
- * any extra fields explicitly (probably here).
- */
- if (rsk->struct_version < 2) {
- /* Danger Will Robinson! Danger! */
- rsk->struct_version = 2;
- return TPM_SUCCESS;
- }
- /*
- * If the CRC is good, we're done. If it's bad, try a couple
- * more times to see if it gets better before we give up. It
- * could just be noise.
- */
- if (rsk->crc8 ==
- vb2_crc8(rsk, offsetof(RollbackSpaceKernel, crc8)))
- return TPM_SUCCESS;
- VB2_DEBUG("TPM: bad CRC\n");
- }
- VB2_DEBUG("TPM: too many bad CRCs, giving up\n");
- return TPM_E_CORRUPTED_STATE;
- }
- uint32_t WriteSpaceKernel(RollbackSpaceKernel *rsk)
- {
- RollbackSpaceKernel rsk2;
- uint32_t r;
- int attempts = 3;
- /* All writes should use struct_version 2 or greater. */
- if (rsk->struct_version < 2)
- rsk->struct_version = 2;
- rsk->crc8 = vb2_crc8(rsk, offsetof(RollbackSpaceKernel, crc8));
- while (attempts--) {
- r = SafeWrite(KERNEL_NV_INDEX, rsk,
- sizeof(RollbackSpaceKernel));
- /* Can't write, not gonna try again */
- if (r != TPM_SUCCESS)
- return r;
- /* Read it back to be sure it got the right values. */
- r = ReadSpaceKernel(&rsk2); /* This checks the CRC */
- if (r == TPM_SUCCESS)
- return r;
- VB2_DEBUG("TPM: bad CRC\n");
- /* Try writing it again. Maybe it was garbled on the way out. */
- }
- VB2_DEBUG("TPM: too many bad CRCs, giving up\n");
- return TPM_E_CORRUPTED_STATE;
- }
- #ifdef DISABLE_ROLLBACK_TPM
- /* Dummy implementations which don't support TPM rollback protection */
- uint32_t RollbackKernelRead(uint32_t* version)
- {
- *version = 0;
- return TPM_SUCCESS;
- }
- uint32_t RollbackKernelWrite(uint32_t version)
- {
- return TPM_SUCCESS;
- }
- uint32_t RollbackKernelLock(int recovery_mode)
- {
- return TPM_SUCCESS;
- }
- uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp)
- {
- memset(fwmp, 0, sizeof(*fwmp));
- return TPM_SUCCESS;
- }
- #else
- uint32_t RollbackKernelRead(uint32_t* version)
- {
- RollbackSpaceKernel rsk;
- /*
- * Read the kernel space and verify its permissions. If the kernel
- * space has the wrong permission, or it doesn't contain the right
- * identifier, we give up. This will need to be fixed by the
- * recovery kernel. We have to worry about this because at any time
- * (even with PP turned off) the TPM owner can remove and redefine a
- * PP-protected space (but not write to it).
- */
- RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
- #ifndef TPM2_MODE
- /*
- * TODO(vbendeb): restore this when it is defined how the kernel space
- * gets protected.
- */
- {
- uint32_t perms, uid;
- RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms));
- memcpy(&uid, &rsk.uid, sizeof(uid));
- if (TPM_NV_PER_PPWRITE != perms ||
- ROLLBACK_SPACE_KERNEL_UID != uid)
- return TPM_E_CORRUPTED_STATE;
- }
- #endif
- memcpy(version, &rsk.kernel_versions, sizeof(*version));
- VB2_DEBUG("TPM: RollbackKernelRead %x\n", (int)*version);
- return TPM_SUCCESS;
- }
- 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);
- }
- uint32_t RollbackKernelLock(int recovery_mode)
- {
- static int kernel_locked = 0;
- uint32_t r;
- if (recovery_mode || kernel_locked)
- return TPM_SUCCESS;
- r = TlclLockPhysicalPresence();
- if (TPM_SUCCESS == r)
- kernel_locked = 1;
- return r;
- }
- uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp)
- {
- union {
- /*
- * Use a union for buf and bf, rather than making bf a pointer
- * to a bare uint8_t[] buffer. This ensures bf will be aligned
- * if necesssary for the target platform.
- */
- uint8_t buf[FWMP_NV_MAX_SIZE];
- struct RollbackSpaceFwmp bf;
- } u;
- uint32_t r;
- int attempts = 3;
- /* Clear destination in case error or FWMP not present */
- memset(fwmp, 0, sizeof(*fwmp));
- while (attempts--) {
- /* Try to read entire 1.0 struct */
- r = TlclRead(FWMP_NV_INDEX, u.buf, sizeof(u.bf));
- if (r == TPM_E_BADINDEX) {
- /* Missing space is not an error; use defaults */
- VB2_DEBUG("TPM: no FWMP space\n");
- return TPM_SUCCESS;
- } else if (r != TPM_SUCCESS) {
- VB2_DEBUG("TPM: read returned 0x%x\n", r);
- return r;
- }
- /*
- * Struct must be at least big enough for 1.0, but not bigger
- * than our buffer size.
- */
- if (u.bf.struct_size < sizeof(u.bf) ||
- u.bf.struct_size > sizeof(u.buf))
- return TPM_E_STRUCT_SIZE;
- /*
- * If space is bigger than we expect, re-read so we properly
- * compute the CRC.
- */
- if (u.bf.struct_size > sizeof(u.bf)) {
- r = TlclRead(FWMP_NV_INDEX, u.buf, u.bf.struct_size);
- if (r != TPM_SUCCESS)
- return r;
- }
- /* Verify CRC */
- if (u.bf.crc != vb2_crc8(u.buf + 2, u.bf.struct_size - 2)) {
- VB2_DEBUG("TPM: bad CRC\n");
- continue;
- }
- /* Verify major version is compatible */
- if ((u.bf.struct_version >> 4) !=
- (ROLLBACK_SPACE_FWMP_VERSION >> 4))
- return TPM_E_STRUCT_VERSION;
- /*
- * 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.bf, sizeof(*fwmp));
- return TPM_SUCCESS;
- }
- VB2_DEBUG("TPM: too many bad CRCs, giving up\n");
- return TPM_E_CORRUPTED_STATE;
- }
- #endif /* DISABLE_ROLLBACK_TPM */
|