diff options
author | Randall Spangler <rspangler@chromium.org> | 2016-04-15 14:49:40 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-05-08 21:16:02 -0700 |
commit | 946abf1439f378dded6d4c4a82b53af86cdf44a3 (patch) | |
tree | 0d21ca10aca695e8ce4c029236d1694c558df507 | |
parent | 111c59b567bf2c1a61bfad38a7db80aa158ef5a0 (diff) | |
download | vboot-946abf1439f378dded6d4c4a82b53af86cdf44a3.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.
Change-Id: Ifaf644c80809552d5961615be6017c2a332a034b
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/339234
-rw-r--r-- | firmware/include/gbb_header.h | 3 | ||||
-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 | 42 | ||||
-rw-r--r-- | tests/vboot_api_kernel4_tests.c | 12 | ||||
-rw-r--r-- | tests/vboot_kernel_tests.c | 39 |
12 files changed, 442 insertions, 5 deletions
diff --git a/firmware/include/gbb_header.h b/firmware/include/gbb_header.h index c7e2a05d..0eef7314 100644 --- a/firmware/include/gbb_header.h +++ b/firmware/include/gbb_header.h @@ -74,6 +74,9 @@ #define GBB_FLAG_FORCE_DEV_BOOT_FASTBOOT_FULL_CAP 0x00002000 /* Enable serial console */ #define GBB_FLAG_ENABLE_SERIAL 0x00004000 +/* 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 ed8ff9b5..edfdc3c4 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 5046d2ae..1a166344 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -124,6 +124,8 @@ enum VbErrorPredefined_t { VBERROR_SCREEN_DRAW = 0x10027, /* failed to jump to RW image */ VBERROR_RW_JUMP_FAILED = 0x10028, + /* 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 da418a33..679da167 100644 --- a/firmware/lib/include/load_kernel_fw.h +++ b/firmware/lib/include/load_kernel_fw.h @@ -22,6 +22,8 @@ /* GPT is external */ #define BOOT_FLAG_EXTERNAL_GPT (0x04ULL) +struct RollbackSpaceFwmp; + typedef struct LoadKernelParams { /* Inputs to LoadKernel() */ /* @@ -57,6 +59,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 49b3a84b..4436df54 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 */ @@ -680,3 +679,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 c3362789..5adeac41 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 /** @@ -280,6 +286,11 @@ 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; @@ -288,6 +299,7 @@ VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) uint32_t allow_usb = 0; uint32_t allow_legacy = 0; + uint32_t disable_dev_boot = 0; uint32_t use_usb = 0; uint32_t use_legacy = 0; uint32_t default_boot = 0; @@ -319,6 +331,46 @@ VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) use_usb = 0; } + /* 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); @@ -1162,12 +1214,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 c6d4e27a..6ab52b7e 100644 --- a/firmware/lib/vboot_kernel.c +++ b/firmware/lib/vboot_kernel.c @@ -15,6 +15,7 @@ #include "gbb_header.h" #include "gpt_misc.h" #include "load_kernel_fw.h" +#include "rollback_index.h" #include "utility.h" #include "vboot_api.h" #include "vboot_common.h" @@ -77,6 +78,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; } @@ -268,6 +273,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 84655fa3..9e9f5b94 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) "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 aebfab64..c0965a65 100644 --- a/tests/vboot_api_kernel2_tests.c +++ b/tests/vboot_api_kernel2_tests.c @@ -46,6 +46,7 @@ static uint32_t mock_num_disks[8]; static uint32_t mock_num_disks_count; extern enum VbEcBootMode_t VbGetMode(void); +extern struct RollbackSpaceFwmp *VbApiKernelGetFwmp(void); /* Reset mock data (for use before each test) */ static void ResetMocks(void) @@ -69,6 +70,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)); @@ -452,6 +455,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; @@ -471,6 +480,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); @@ -481,6 +497,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 6cdd91b7..c1ba3d9d 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 e690bc31..16cf1a9f 100644 --- a/tests/vboot_kernel_tests.c +++ b/tests/vboot_kernel_tests.c @@ -13,10 +13,12 @@ #include "cgptlib.h" #include "cgptlib_internal.h" #include "crc32.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" @@ -63,12 +65,13 @@ static LoadKernelParams lkp; static VbKeyBlockHeader kbh; static VbKernelPreambleHeader kph; static VbCommonParams cparams; +static struct RollbackSpaceFwmp fwmp; static uint8_t mock_disk[MOCK_SECTOR_SIZE * MOCK_SECTOR_COUNT]; static GptHeader *mock_gpt_primary = (GptHeader*)&mock_disk[MOCK_SECTOR_SIZE * 1]; static GptHeader *mock_gpt_secondary = (GptHeader*)&mock_disk[MOCK_SECTOR_SIZE * (MOCK_SECTOR_COUNT - 1)]; - +static uint8_t mock_digest[SHA256_DIGEST_SIZE] = {12, 34, 56, 78}; /** * Prepare a valid GPT header that will pass CheckHeader() tests @@ -172,6 +175,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 */ @@ -287,6 +293,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 @@ -640,6 +653,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 = @@ -725,6 +746,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, |