diff options
author | Randall Spangler <rspangler@chromium.org> | 2016-04-15 14:49:40 -0700 |
---|---|---|
committer | Randall Spangler <rspangler@chromium.org> | 2016-07-29 17:30:55 +0000 |
commit | b1d75add014b7383e00961156ce51749e6507465 (patch) | |
tree | 2d6692b7905ac6f6061a7385c2c9a8dcea49758f | |
parent | b68a08bd257ad25c3be3ddb2c1c03b93aad68bb9 (diff) | |
download | vboot-b1d75add014b7383e00961156ce51749e6507465.tar.gz |
vboot: Add firmware management parameters
This adds RW firmware support for the optional firmware management
parameters TPM space.
System-level tests require CL:339262 to add cryptohome support.
BUG=chromium:601492
BRANCH=baytrail and newer platforms
TEST=make -j runtests
Or better, COV=1 make, and then make sure all new code is covered.
Additional manual tests. MUST use a test image for these, because a
test image has a root shell even with dev mode disabled:
Set FWMP:
crossystem clear_tpm_owner_request=1
reboot
cryptohome --action=tpm_take_ownership
cryptohome --action=tpm_wait_ownership
cryptohome --action=set_firmware_management_parameters --flags=1
cryptohome --action=get_firmware_management_parameters
Reboot system with power+refresh+esc
Use Ctrl+D then Enter to enable dev mode.
Goes to the TONORM screen. Â
Pressing Esc doesn't exit it.
Pressing Enter turns dev mode off.
Then let it boot.
Just to make sure FWMP did get set persistently:
cryptohome --action=get_firmware_management_parameters
Now remove the FWMP
crossystem clear_tpm_owner_request=1
reboot
cryptohome --action=tpm_take_ownership
cryptohome --action=tpm_wait_ownership
cryptohome --action=remove_firmware_management_parameters
Reboot system with power+refresh+esc
Use Ctrl+D then Enter to enable dev mode.
Goes to the DEV screen.
Change-Id: I0ac31bb2c64671ee9c3c810174baaf02b4cce641
Original-Change-Id: Ifaf644c80809552d5961615be6017c2a332a034b
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/339234
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/356790
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Reviewed-by: Stefan Reinauer <reinauer@google.com>
-rw-r--r-- | firmware/include/gbb_header.h | 2 | ||||
-rw-r--r-- | firmware/include/tss_constants.h | 2 | ||||
-rw-r--r-- | firmware/include/vboot_api.h | 2 | ||||
-rw-r--r-- | firmware/lib/include/load_kernel_fw.h | 4 | ||||
-rw-r--r-- | firmware/lib/include/rollback_index.h | 40 | ||||
-rw-r--r-- | firmware/lib/rollback_index.c | 71 | ||||
-rw-r--r-- | firmware/lib/vboot_api_kernel.c | 71 | ||||
-rw-r--r-- | firmware/lib/vboot_kernel.c | 38 | ||||
-rw-r--r-- | tests/rollback_index2_tests.c | 123 | ||||
-rw-r--r-- | tests/vboot_api_kernel2_tests.c | 43 | ||||
-rw-r--r-- | tests/vboot_api_kernel4_tests.c | 12 | ||||
-rw-r--r-- | tests/vboot_kernel_tests.c | 38 |
12 files changed, 442 insertions, 4 deletions
diff --git a/firmware/include/gbb_header.h b/firmware/include/gbb_header.h index 86aa12a6..b600f120 100644 --- a/firmware/include/gbb_header.h +++ b/firmware/include/gbb_header.h @@ -62,6 +62,8 @@ #define GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC 0x00000200 /* Default to booting legacy OS when dev screen times out */ #define GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY 0x00000400 +/* Disable using FWMP */ +#define GBB_FLAG_DISABLE_FWMP 0x00008000 #ifdef __cplusplus extern "C" { diff --git a/firmware/include/tss_constants.h b/firmware/include/tss_constants.h index 883a5ad8..b2ddd7db 100644 --- a/firmware/include/tss_constants.h +++ b/firmware/include/tss_constants.h @@ -40,6 +40,8 @@ #define TPM_E_WRITE_FAILURE ((uint32_t)0x00005008) /* vboot local */ #define TPM_E_READ_EMPTY ((uint32_t)0x00005009) /* vboot local */ #define TPM_E_READ_FAILURE ((uint32_t)0x0000500a) /* vboot local */ +#define TPM_E_STRUCT_SIZE ((uint32_t) 0x0000500b) /* vboot local */ +#define TPM_E_STRUCT_VERSION ((uint32_t) 0x0000500c) /* vboot local */ #define TPM_NV_INDEX0 ((uint32_t)0x00000000) #define TPM_NV_INDEX_LOCK ((uint32_t)0xffffffff) diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h index 0ae6c76b..f3cc9138 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -112,6 +112,8 @@ enum VbErrorPredefined_t { VBERROR_UNSUPPORTED_REGION = 0x10025, /* No image present (returned from VbGbbReadImage() for missing image) */ VBERROR_NO_IMAGE_PRESENT = 0x10026, + /* Error reading FWMP from TPM (note: not present is not an error) */ + VBERROR_TPM_READ_FWMP = 0x10029, /* VbExEcGetExpectedRWHash() may return the following codes */ /* Compute expected RW hash from the EC image; BIOS doesn't have it */ diff --git a/firmware/lib/include/load_kernel_fw.h b/firmware/lib/include/load_kernel_fw.h index a710ee5d..1e258a23 100644 --- a/firmware/lib/include/load_kernel_fw.h +++ b/firmware/lib/include/load_kernel_fw.h @@ -20,6 +20,8 @@ /* In recovery mode */ #define BOOT_FLAG_RECOVERY (0x02ULL) +struct RollbackSpaceFwmp; + typedef struct LoadKernelParams { /* Inputs to LoadKernel() */ /* @@ -53,6 +55,8 @@ typedef struct LoadKernelParams { * VbNvSetup() and VbNvTeardown() on the context. */ VbNvContext *nv_context; + /* Firmware management parameters; may be NULL if not present. */ + const struct RollbackSpaceFwmp *fwmp; /* * Outputs from LoadKernel(); valid only if LoadKernel() returns diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h index dd0de32a..6cc9ee61 100644 --- a/firmware/lib/include/rollback_index.h +++ b/firmware/lib/include/rollback_index.h @@ -18,7 +18,8 @@ /* This is just an opaque space for backup purposes */ #define BACKUP_NV_INDEX 0x1009 #define BACKUP_NV_SIZE 16 - +#define FWMP_NV_INDEX 0x100a +#define FWMP_NV_MAX_SIZE 128 /* Structure definitions for TPM spaces */ @@ -70,6 +71,34 @@ typedef struct RollbackSpaceFirmware { uint8_t crc8; } __attribute__((packed)) RollbackSpaceFirmware; +#define FWMP_HASH_SIZE 32 /* Enough for SHA-256 */ + +/* Firmware management parameters */ +struct RollbackSpaceFwmp { + /* CRC-8 of fields following struct_size */ + uint8_t crc; + /* Structure size in bytes */ + uint8_t struct_size; + /* Structure version */ + uint8_t struct_version; + /* Reserved; ignored by current reader */ + uint8_t reserved0; + /* Flags; see enum fwmp_flags */ + uint32_t flags; + /* Hash of developer kernel key */ + uint8_t dev_key_hash[FWMP_HASH_SIZE]; +} __attribute__((packed)); + +#define ROLLBACK_SPACE_FWMP_VERSION 0x10 /* 1.0 */ + +enum fwmp_flags { + FWMP_DEV_DISABLE_BOOT = (1 << 0), + FWMP_DEV_DISABLE_RECOVERY = (1 << 1), + FWMP_DEV_ENABLE_USB = (1 << 2), + FWMP_DEV_ENABLE_LEGACY = (1 << 3), + FWMP_DEV_ENABLE_OFFICIAL_ONLY = (1 << 4), + FWMP_DEV_USE_KEY_HASH = (1 << 5), +}; /* All functions return TPM_SUCCESS (zero) if successful, non-zero if error */ @@ -134,6 +163,15 @@ uint32_t RollbackBackupWrite(uint8_t *raw); */ uint32_t RollbackKernelLock(int recovery_mode); +/** + * Read and validate firmware management parameters. + * + * Absence of a FWMP is not an error; in this case, fwmp will be cleared. + * + * Returns non-zero if error. + */ +uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp); + /****************************************************************************/ /* diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c index 306e9032..8aec1629 100644 --- a/firmware/lib/rollback_index.c +++ b/firmware/lib/rollback_index.c @@ -481,7 +481,6 @@ uint32_t SetupTPM(int developer_mode, int disable_dev_request, return TPM_SUCCESS; } - #ifdef DISABLE_ROLLBACK_TPM /* Dummy implementations which don't support TPM rollback protection */ @@ -674,3 +673,73 @@ uint32_t RollbackKernelLock(int recovery_mode) } #endif /* DISABLE_ROLLBACK_TPM */ + +uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp) +{ + uint8_t buf[FWMP_NV_MAX_SIZE]; + struct RollbackSpaceFwmp *bf = (struct RollbackSpaceFwmp *)buf; + 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, buf, sizeof(*bf)); + if (r == TPM_E_BADINDEX) { + /* Missing space is not an error; use defaults */ + VBDEBUG(("TPM: %s() - no FWMP space\n", __func__)); + return TPM_SUCCESS; + } else if (r != TPM_SUCCESS) { + VBDEBUG(("TPM: %s() - read returned 0x%x\n", + __func__, r)); + return r; + } + + /* + * Struct must be at least big enough for 1.0, but not bigger + * than our buffer size. + */ + if (bf->struct_size < sizeof(*bf) || + bf->struct_size > sizeof(buf)) + return TPM_E_STRUCT_SIZE; + + /* + * If space is bigger than we expect, re-read so we properly + * compute the CRC. + */ + if (bf->struct_size > sizeof(*bf)) { + r = TlclRead(FWMP_NV_INDEX, buf, bf->struct_size); + if (r != TPM_SUCCESS) + return r; + } + + /* Verify CRC */ + if (bf->crc != Crc8(buf + 2, bf->struct_size - 2)) { + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + continue; + } + + /* Verify major version is compatible */ + if ((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, bf, sizeof(*fwmp)); + return TPM_SUCCESS; + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; +} + diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index 18d00ae7..1a7cbe7d 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.c @@ -22,14 +22,20 @@ /* Global variables */ static VbNvContext vnc; +static struct RollbackSpaceFwmp fwmp; #ifdef CHROMEOS_ENVIRONMENT -/* Global variable accessor for unit tests */ +/* Global variable accessors for unit tests */ VbNvContext *VbApiKernelGetVnc(void) { return &vnc; } + +struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void) +{ + return &fwmp; +} #endif /** @@ -173,12 +179,18 @@ VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p) return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED); } +static const char dev_disable_msg[] = + "Developer mode is disabled on this device by system policy.\n" + "For more information, see http://dev.chromium.org/chromium-os/fwmp\n" + "\n"; + VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) { GoogleBinaryBlockHeader *gbb = cparams->gbb; VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; uint32_t allow_usb = 0, allow_legacy = 0, ctrl_d_pressed = 0; + uint32_t disable_dev_boot = 0; VbAudioContext *audio = 0; VBDEBUG(("Entering %s()\n", __func__)); @@ -193,6 +205,46 @@ VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY) allow_legacy = 1; + /* Handle FWMP override */ + if (fwmp.flags & FWMP_DEV_ENABLE_USB) + allow_usb = 1; + if (fwmp.flags & FWMP_DEV_ENABLE_LEGACY) + allow_legacy = 1; + if (fwmp.flags & FWMP_DEV_DISABLE_BOOT) { + if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) { + VBDEBUG(("%s() - FWMP_DEV_DISABLE_BOOT rejected by " + "FORCE_DEV_SWITCH_ON\n", + __func__)); + } else { + disable_dev_boot = 1; + } + } + + /* If dev mode is disabled, only allow TONORM */ + while (disable_dev_boot) { + VBDEBUG(("%s() - dev_disable_boot is set.\n", __func__)); + VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_TO_NORM, 0, &vnc); + VbExDisplayDebugInfo(dev_disable_msg); + + /* Ignore space in VbUserConfirms()... */ + switch (VbUserConfirms(cparams, 0)) { + case 1: + VBDEBUG(("%s() - leaving dev-mode.\n", __func__)); + VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1); + VbDisplayScreen(cparams, + VB_SCREEN_TO_NORM_CONFIRMED, + 0, &vnc); + VbExSleepMs(5000); + return VBERROR_TPM_REBOOT_REQUIRED; + case -1: + VBDEBUG(("%s() - shutdown requested\n", __func__)); + return VBERROR_SHUTDOWN_REQUESTED; + default: + /* Ignore user attempt to cancel */ + VBDEBUG(("%s() - ignore cancel TONORM\n", __func__)); + } + } + /* Show the dev mode warning screen */ VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); @@ -907,12 +959,29 @@ VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, } shared->kernel_version_tpm_start = shared->kernel_version_tpm; + /* Read FWMP. Ignore errors in recovery mode. */ + if (cparams->gbb->flags & GBB_FLAG_DISABLE_FWMP) { + Memset(&fwmp, 0, sizeof(fwmp)); + tpm_status = 0; + } else { + tpm_status = RollbackFwmpRead(&fwmp); + } + if (0 != tpm_status) { + VBDEBUG(("Unable to get FWMP from TPM\n")); + if (!shared->recovery_reason) { + VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR); + retval = VBERROR_TPM_READ_FWMP; + goto VbSelectAndLoadKernel_exit; + } + } + /* Fill in params for calls to LoadKernel() */ Memset(&p, 0, sizeof(p)); p.shared_data_blob = cparams->shared_data_blob; p.shared_data_size = cparams->shared_data_size; p.gbb_data = cparams->gbb_data; p.gbb_size = cparams->gbb_size; + p.fwmp = &fwmp; /* * This could be set to NULL, in which case the vboot header diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c index 72cd2cc1..10e2ccb4 100644 --- a/firmware/lib/vboot_kernel.c +++ b/firmware/lib/vboot_kernel.c @@ -14,6 +14,7 @@ #include "gbb_access.h" #include "gbb_header.h" #include "load_kernel_fw.h" +#include "rollback_index.h" #include "utility.h" #include "vboot_api.h" #include "vboot_common.h" @@ -206,6 +207,10 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams) } else if (dev_switch) { boot_mode = kBootDev; VbNvGet(vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); + + if (params->fwmp && + (params->fwmp->flags & FWMP_DEV_ENABLE_OFFICIAL_ONLY)) + require_official_os = 1; } else { boot_mode = kBootNormal; } @@ -395,6 +400,39 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams) goto bad_kernel; } + + /* If in developer mode and using key hash, check it */ + if ((kBootDev == boot_mode) && + params->fwmp && + (params->fwmp->flags & FWMP_DEV_USE_KEY_HASH)) { + VbPublicKey *key = &key_block->data_key; + uint8_t *buf = ((uint8_t *)key) + key->key_offset; + uint64_t buflen = key->key_size; + uint8_t *digest; + + VBDEBUG(("Checking developer key hash.\n")); + digest = DigestBuf(buf, buflen, + SHA256_DIGEST_ALGORITHM); + if (0 != SafeMemcmp(digest, params->fwmp->dev_key_hash, + SHA256_DIGEST_SIZE)) { + int i; + + VBDEBUG(("Wrong developer key hash.\n")); + VBDEBUG(("Want: ")); + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + VBDEBUG(("%02x", + params->fwmp->dev_key_hash[i])); + VBDEBUG(("\nGot: ")); + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + VBDEBUG(("%02x", digest[i])); + VBDEBUG(("\n")); + + VbExFree(digest); + goto bad_kernel; + } + VbExFree(digest); + } + /* Get key for preamble/data verification from the key block. */ data_key = PublicKeyToRSA(&key_block->data_key); if (!data_key) { diff --git a/tests/rollback_index2_tests.c b/tests/rollback_index2_tests.c index 853bfc86..fb2f2e44 100644 --- a/tests/rollback_index2_tests.c +++ b/tests/rollback_index2_tests.c @@ -53,8 +53,21 @@ static int noise_on[MAX_NOISE_COUNT]; /* calls to inject noise on */ static TPM_PERMANENT_FLAGS mock_pflags; static RollbackSpaceFirmware mock_rsf; static RollbackSpaceKernel mock_rsk; + +static union { + struct RollbackSpaceFwmp fwmp; + uint8_t buf[FWMP_NV_MAX_SIZE]; +} mock_fwmp; + static uint32_t mock_permissions; +/* Recalculate CRC of FWMP data */ +static void RecalcFwmpCrc(void) +{ + mock_fwmp.fwmp.crc = Crc8(mock_fwmp.buf + 2, + mock_fwmp.fwmp.struct_size - 2); +} + /* Reset the variables for the Tlcl mock functions. */ static void ResetMocks(int fail_on_call, uint32_t fail_with_err) { @@ -70,6 +83,15 @@ static void ResetMocks(int fail_on_call, uint32_t fail_with_err) Memset(&mock_rsf, 0, sizeof(mock_rsf)); Memset(&mock_rsk, 0, sizeof(mock_rsk)); mock_permissions = 0; + + Memset(mock_fwmp.buf, 0, sizeof(mock_fwmp.buf)); + mock_fwmp.fwmp.struct_size = sizeof(mock_fwmp.fwmp); + mock_fwmp.fwmp.struct_version = ROLLBACK_SPACE_FWMP_VERSION; + mock_fwmp.fwmp.flags = 0x1234; + /* Put some data in the hash */ + mock_fwmp.fwmp.dev_key_hash[0] = 0xaa; + mock_fwmp.fwmp.dev_key_hash[FWMP_HASH_SIZE - 1] = 0xbb; + RecalcFwmpCrc(); } /****************************************************************************/ @@ -135,6 +157,12 @@ uint32_t TlclRead(uint32_t index, void* data, uint32_t length) TEST_EQ(length, sizeof(mock_rsk), "TlclRead rsk size"); Memcpy(data, &mock_rsk, length); MaybeInjectNoise(data, length); + } else if (FWMP_NV_INDEX == index) { + Memset(data, 0, length); + if (length > sizeof(mock_fwmp)) + length = sizeof(mock_fwmp); + Memcpy(data, &mock_fwmp, length); + MaybeInjectNoise(data, length); } else { Memset(data, 0, length); } @@ -962,6 +990,7 @@ static void RollbackKernelTest(void) TEST_STR_EQ(mock_calls, "", "no tlcl calls"); } +/****************************************************************************/ /* Tests for RollbackS3Resume() */ static void RollbackS3ResumeTest(void) { @@ -982,6 +1011,99 @@ static void RollbackS3ResumeTest(void) "RollbackS3Resume() other error"); } +/****************************************************************************/ +/* Tests for RollbackFwmpRead() calls */ + +static void RollbackFwmpTest(void) +{ + struct RollbackSpaceFwmp fwmp; + struct RollbackSpaceFwmp fwmp_zero = {0}; + + /* Normal read */ + ResetMocks(0, 0); + TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead()"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x100a, 40)\n", + " tlcl calls"); + TEST_EQ(0, Memcmp(&fwmp, &mock_fwmp, sizeof(fwmp)), " data"); + + /* Read error */ + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_IOERROR, + "RollbackFwmpRead() error"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x100a, 40)\n", + " tlcl calls"); + TEST_EQ(0, Memcmp(&fwmp, &fwmp_zero, sizeof(fwmp)), " data clear"); + + /* Not present isn't an error; just returns empty data */ + ResetMocks(1, TPM_E_BADINDEX); + TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead() not present"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x100a, 40)\n", + " tlcl calls"); + TEST_EQ(0, Memcmp(&fwmp, &fwmp_zero, sizeof(fwmp)), " data clear"); + + /* Struct size too small */ + ResetMocks(0, 0); + mock_fwmp.fwmp.struct_size--; + TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_STRUCT_SIZE, + "RollbackFwmpRead() too small"); + + /* Struct size too large with good CRC */ + ResetMocks(0, 0); + mock_fwmp.fwmp.struct_size += 4; + RecalcFwmpCrc(); + TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead() bigger"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x100a, 40)\n" + "TlclRead(0x100a, 44)\n", + " tlcl calls"); + TEST_EQ(0, Memcmp(&fwmp, &mock_fwmp, sizeof(fwmp)), " data"); + + /* Bad CRC causes retry, then eventual failure */ + ResetMocks(0, 0); + mock_fwmp.fwmp.crc++; + TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_CORRUPTED_STATE, + "RollbackFwmpRead() crc"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x100a, 40)\n" + "TlclRead(0x100a, 40)\n" + "TlclRead(0x100a, 40)\n", + " tlcl calls"); + + /* Struct size too large with bad CRC */ + ResetMocks(0, 0); + mock_fwmp.fwmp.struct_size += 4; + RecalcFwmpCrc(); + mock_fwmp.fwmp.crc++; + TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_CORRUPTED_STATE, + "RollbackFwmpRead() bigger crc"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x100a, 40)\n" + "TlclRead(0x100a, 44)\n" + "TlclRead(0x100a, 40)\n" + "TlclRead(0x100a, 44)\n" + "TlclRead(0x100a, 40)\n" + "TlclRead(0x100a, 44)\n", + " tlcl calls"); + TEST_EQ(0, Memcmp(&fwmp, &fwmp_zero, sizeof(fwmp)), " data"); + + /* Minor version difference ok */ + ResetMocks(0, 0); + mock_fwmp.fwmp.struct_version++; + RecalcFwmpCrc(); + TEST_EQ(RollbackFwmpRead(&fwmp), 0, "RollbackFwmpRead() minor version"); + TEST_EQ(0, Memcmp(&fwmp, &mock_fwmp, sizeof(fwmp)), " data"); + + /* Major version difference not ok */ + ResetMocks(0, 0); + mock_fwmp.fwmp.struct_version += 0x10; + RecalcFwmpCrc(); + TEST_EQ(RollbackFwmpRead(&fwmp), TPM_E_STRUCT_VERSION, + "RollbackFwmpRead() major version"); +} + int main(int argc, char* argv[]) { CrcTestFirmware(); @@ -992,6 +1114,7 @@ int main(int argc, char* argv[]) RollbackFirmwareTest(); RollbackKernelTest(); RollbackS3ResumeTest(); + RollbackFwmpTest(); return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c index 21ea3063..44fdec50 100644 --- a/tests/vboot_api_kernel2_tests.c +++ b/tests/vboot_api_kernel2_tests.c @@ -42,6 +42,8 @@ static uint32_t screens_count = 0; static uint32_t mock_num_disks[8]; static uint32_t mock_num_disks_count; +extern struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void); + /* Reset mock data (for use before each test) */ static void ResetMocks(void) { @@ -64,6 +66,8 @@ static void ResetMocks(void) VbNvSetup(VbApiKernelGetVnc()); VbNvTeardown(VbApiKernelGetVnc()); /* So CRC gets generated */ + Memset(VbApiKernelGetFwmp(), 0, sizeof(struct RollbackSpaceFwmp)); + Memset(&shared_data, 0, sizeof(shared_data)); VbSharedDataInit(shared, sizeof(shared_data)); @@ -348,6 +352,12 @@ static void VbBootDevTest(void) TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L nv legacy"); TEST_EQ(vbexlegacy_called, 1, " try legacy"); + ResetMocks(); + VbApiKernelGetFwmp()->flags |= FWMP_DEV_ENABLE_LEGACY; + mock_keypress[0] = 0x0c; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L fwmp legacy"); + TEST_EQ(vbexlegacy_called, 1, " fwmp legacy"); + /* Ctrl+U boots USB only if enabled */ ResetMocks(); mock_keypress[0] = 0x15; @@ -367,6 +377,13 @@ static void VbBootDevTest(void) vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE; TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U force USB"); + /* Ctrl+U enabled via FWMP */ + ResetMocks(); + VbApiKernelGetFwmp()->flags |= FWMP_DEV_ENABLE_USB; + mock_keypress[0] = 0x15; + vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U force USB"); + /* If no USB, eventually times out and tries fixed disk */ ResetMocks(); VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_USB, 1); @@ -377,6 +394,32 @@ static void VbBootDevTest(void) TEST_EQ(u, 0, " recovery reason"); TEST_EQ(audio_looping_calls_left, 0, " used up audio"); + /* If dev mode is disabled, goes to TONORM screen repeatedly */ + ResetMocks(); + VbApiKernelGetFwmp()->flags |= FWMP_DEV_DISABLE_BOOT; + mock_keypress[0] = '\x1b'; /* Just causes TONORM again */ + mock_keypress[1] = '\r'; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_TPM_REBOOT_REQUIRED, + "FWMP dev disabled"); + TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_TO_NORM, + " tonorm screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_DEVELOPER_TO_NORM, + " tonorm screen"); + TEST_EQ(screens_displayed[2], VB_SCREEN_TO_NORM_CONFIRMED, + " confirm screen"); + VbNvGet(VbApiKernelGetVnc(), VBNV_DISABLE_DEV_REQUEST, &u); + TEST_EQ(u, 1, " disable dev request"); + + /* Shutdown requested when dev disabled */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + VbApiKernelGetFwmp()->flags |= FWMP_DEV_DISABLE_BOOT; + shutdown_request_calls_left = 1; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Shutdown requested when dev disabled"); + TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_TO_NORM, + " tonorm screen"); + printf("...done.\n"); } diff --git a/tests/vboot_api_kernel4_tests.c b/tests/vboot_api_kernel4_tests.c index bb827024..27c4ef7c 100644 --- a/tests/vboot_api_kernel4_tests.c +++ b/tests/vboot_api_kernel4_tests.c @@ -31,7 +31,8 @@ static GoogleBinaryBlockHeader gbb; static int ecsync_retval; static uint32_t rkr_version; static uint32_t new_version; -static int rkr_retval, rkw_retval, rkl_retval; +static struct RollbackSpaceFwmp rfr_fwmp; +static int rkr_retval, rkw_retval, rkl_retval, rfr_retval; static VbError_t vbboot_retval; /* Reset mock data (for use before each test) */ @@ -57,6 +58,9 @@ static void ResetMocks(void) Memset(&shared_data, 0, sizeof(shared_data)); VbSharedDataInit(shared, sizeof(shared_data)); + Memset(&rfr_fwmp, 0, sizeof(rfr_fwmp)); + rfr_retval = TPM_SUCCESS; + ecsync_retval = VBERROR_SUCCESS; rkr_version = new_version = 0x10002; rkr_retval = rkw_retval = rkl_retval = VBERROR_SUCCESS; @@ -100,6 +104,12 @@ uint32_t RollbackKernelLock(int recovery_mode) return rkl_retval; } +uint32_t RollbackFwmpRead(struct RollbackSpaceFwmp *fwmp) +{ + Memcpy(fwmp, &rfr_fwmp, sizeof(*fwmp)); + return rfr_retval; +} + VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p) { shared->kernel_version_tpm = new_version; diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c index ee164cea..9f13f3cf 100644 --- a/tests/vboot_kernel_tests.c +++ b/tests/vboot_kernel_tests.c @@ -11,10 +11,12 @@ #include <string.h> #include "cgptlib.h" +#include "cryptolib.h" #include "gbb_header.h" #include "gpt.h" #include "host_common.h" #include "load_kernel_fw.h" +#include "rollback_index.h" #include "test_common.h" #include "vboot_api.h" #include "vboot_common.h" @@ -57,6 +59,8 @@ static LoadKernelParams lkp; static VbKeyBlockHeader kbh; static VbKernelPreambleHeader kph; static VbCommonParams cparams; +static struct RollbackSpaceFwmp fwmp; +static uint8_t mock_digest[SHA256_DIGEST_SIZE] = {12, 34, 56, 78}; static void ResetCallLog(void) { @@ -121,6 +125,9 @@ static void ResetMocks(void) kph.bootloader_address = 0xbeadd008; kph.bootloader_size = 0x1234; + memset(&fwmp, 0, sizeof(fwmp)); + memcpy(fwmp.dev_key_hash, mock_digest, sizeof(fwmp.dev_key_hash)); + memset(mock_parts, 0, sizeof(mock_parts)); mock_parts[0].start = 100; mock_parts[0].size = 150; /* 75 KB */ @@ -229,6 +236,13 @@ int VerifyData(const uint8_t *data, uint64_t size, const VbSignature *sig, return VBERROR_SUCCESS; } +uint8_t* DigestBuf(const uint8_t* buf, uint64_t len, int sig_algorithm) +{ + uint8_t *d = VbExMalloc(sizeof(mock_digest)); + + memcpy(d, mock_digest, sizeof(mock_digest)); + return d; +} /** * Test reading/writing GPT @@ -442,6 +456,14 @@ static void LoadKernelTest(void) TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND, "Fail key block dev sig"); + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + lkp.fwmp = &fwmp; + fwmp.flags |= FWMP_DEV_ENABLE_OFFICIAL_ONLY; + key_block_verify_fail = 1; + TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND, + "Fail key block dev sig fwmp"); + /* Check key block flag mismatches */ ResetMocks(); kbh.key_block_flags = @@ -527,6 +549,22 @@ static void LoadKernelTest(void) lkp.boot_flags |= BOOT_FLAG_RECOVERY; TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Kernel version ignored in rec mode"); + /* Check developer key hash - bad */ + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + lkp.fwmp = &fwmp; + fwmp.flags |= FWMP_DEV_USE_KEY_HASH; + fwmp.dev_key_hash[0]++; + TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND, + "Fail key block dev fwmp hash"); + + /* Check developer key hash - good */ + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + lkp.fwmp = &fwmp; + fwmp.flags |= FWMP_DEV_USE_KEY_HASH; + TEST_EQ(LoadKernel(&lkp, &cparams), 0, "Good key block dev fwmp hash"); + ResetMocks(); kph.preamble_size |= 0x07; TEST_EQ(LoadKernel(&lkp, &cparams), VBERROR_INVALID_KERNEL_FOUND, |