diff options
author | Daisuke Nojiri <dnojiri@chromium.org> | 2014-07-02 08:37:23 -0700 |
---|---|---|
committer | Marc Jones <marc.jones@se-eng.com> | 2015-01-27 01:43:57 +0100 |
commit | efb5cde87fb0afef7a0e3c1c6bf3a7cc9cb8dbeb (patch) | |
tree | beb9af9b1610e26fdc7f606643f36d0c196e052a /src | |
parent | 3c21e4687bae7169ee8c3b07cad095cf35106ecb (diff) | |
download | coreboot-efb5cde87fb0afef7a0e3c1c6bf3a7cc9cb8dbeb.tar.xz |
vboot2: copy tlcl from vboot_reference as a preparation for vboot2 integration
vboot2 abtracts tpm storage as some 'secure' space. Thus, it's firmware's
responsibility to handle vboot specific operations with tpm. This CL just copies
related files from vboot_reference so that we can see how code was modified in
the next CL. Note rollback_index.c/h were renamed to antirollback.c/h.
TEST=none
BUG=none
Branch=none
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Original-Change-Id: I1792a622058f70a8fcd3c4037547539ad2870420
Original-Reviewed-on: https://chromium-review.googlesource.com/206462
Original-Commit-Queue: Daisuke Nojiri <dnojiri@chromium.org>
Original-Tested-by: Daisuke Nojiri <dnojiri@chromium.org>
Original-Reviewed-by: Randall Spangler <rspangler@chromium.org>
(cherry picked from commit 2ae188b29242bf09c5e79e31f98b330a30bf7b93)
Signed-off-by: Marc Jones <marc.jones@se-eng.com>
Change-Id: I5fa5a636003e8472127194e961fea4309489b1d9
Reviewed-on: http://review.coreboot.org/8164
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src')
-rw-r--r-- | src/include/antirollback.h | 187 | ||||
-rw-r--r-- | src/include/tpm_lite/tlcl.h | 209 | ||||
-rw-r--r-- | src/include/tpm_lite/tss_constants.h | 96 | ||||
-rw-r--r-- | src/lib/tlcl.c | 466 | ||||
-rw-r--r-- | src/lib/tlcl_internal.h | 61 | ||||
-rw-r--r-- | src/lib/tlcl_structures.h | 138 | ||||
-rw-r--r-- | src/lib/tpm_error_messages.h | 250 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/antirollback.c | 676 |
8 files changed, 2083 insertions, 0 deletions
diff --git a/src/include/antirollback.h b/src/include/antirollback.h new file mode 100644 index 0000000000..dd0de32aef --- /dev/null +++ b/src/include/antirollback.h @@ -0,0 +1,187 @@ +/* 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. + */ + +#ifndef VBOOT_REFERENCE_ROLLBACK_INDEX_H_ +#define VBOOT_REFERENCE_ROLLBACK_INDEX_H_ + +#include "sysincludes.h" +#include "tss_constants.h" + +/* TPM NVRAM location indices. */ +#define FIRMWARE_NV_INDEX 0x1007 +#define KERNEL_NV_INDEX 0x1008 +/* This is just an opaque space for backup purposes */ +#define BACKUP_NV_INDEX 0x1009 +#define BACKUP_NV_SIZE 16 + + +/* Structure definitions for TPM spaces */ + +/* Kernel space - KERNEL_NV_INDEX, locked with physical presence. */ +#define ROLLBACK_SPACE_KERNEL_VERSION 2 +#define ROLLBACK_SPACE_KERNEL_UID 0x4752574C /* 'GRWL' */ + +typedef struct RollbackSpaceKernel { + /* Struct version, for backwards compatibility */ + uint8_t struct_version; + /* Unique ID to detect space redefinition */ + uint32_t uid; + /* Kernel versions */ + uint32_t kernel_versions; + /* Reserved for future expansion */ + uint8_t reserved[3]; + /* Checksum (v2 and later only) */ + uint8_t crc8; +} __attribute__((packed)) RollbackSpaceKernel; + +/* Flags for firmware space */ +/* + * Last boot was developer mode. TPM ownership is cleared when transitioning + * to/from developer mode. + */ +#define FLAG_LAST_BOOT_DEVELOPER 0x01 +/* + * Some systems may not have a dedicated dev-mode switch, but enter and leave + * dev-mode through some recovery-mode magic keypresses. For those systems, the + * dev-mode "switch" state is in this bit (0=normal, 1=dev). To make it work, a + * new flag is passed to VbInit(), indicating that the system lacks a physical + * dev-mode switch. If a physical switch is present, this bit is ignored. + */ +#define FLAG_VIRTUAL_DEV_MODE_ON 0x02 + +/* Firmware space - FIRMWARE_NV_INDEX, locked with global lock. */ +#define ROLLBACK_SPACE_FIRMWARE_VERSION 2 + +typedef struct RollbackSpaceFirmware { + /* Struct version, for backwards compatibility */ + uint8_t struct_version; + /* Flags (see FLAG_* above) */ + uint8_t flags; + /* Firmware versions */ + uint32_t fw_versions; + /* Reserved for future expansion */ + uint8_t reserved[3]; + /* Checksum (v2 and later only) */ + uint8_t crc8; +} __attribute__((packed)) RollbackSpaceFirmware; + + +/* All functions return TPM_SUCCESS (zero) if successful, non-zero if error */ + +/* + * These functions are called from VbInit(). They cannot use global + * variables. + */ + +uint32_t RollbackS3Resume(void); + +/* + * These functions are callable from VbSelectFirmware(). They cannot use + * global variables. + */ + +/** + * This must be called. + */ +uint32_t RollbackFirmwareSetup(int is_hw_dev, + int disable_dev_request, + int clear_tpm_owner_request, + /* two outputs on success */ + int *is_virt_dev, uint32_t *tpm_version); + +/** + * Write may be called if the versions change. + */ +uint32_t RollbackFirmwareWrite(uint32_t version); + +/** + * Lock must be called. + */ +uint32_t RollbackFirmwareLock(void); + +/* + * These functions are callable from VbSelectAndLoadKernel(). They may use + * global variables. + */ + +/** + * Read stored kernel version. + */ +uint32_t RollbackKernelRead(uint32_t *version); + +/** + * Write stored kernel version. + */ +uint32_t RollbackKernelWrite(uint32_t version); + +/** + * Read backup data. + */ +uint32_t RollbackBackupRead(uint8_t *raw); + +/** + * Write backup data. + */ +uint32_t RollbackBackupWrite(uint8_t *raw); + +/** + * Lock must be called. Internally, it's ignored in recovery mode. + */ +uint32_t RollbackKernelLock(int recovery_mode); + +/****************************************************************************/ + +/* + * The following functions are internal apis, listed here for use by unit tests + * only. + */ + +/** + * Issue a TPM_Clear and reenable/reactivate the TPM. + */ +uint32_t TPMClearAndReenable(void); + +/** + * 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 SafeWrite(uint32_t index, const void *data, uint32_t length); + +/** + * Similarly to SafeWrite(), this ensures we don't fail a DefineSpace because + * we hit the TPM write limit. This is even less likely to happen than with + * writes because we only define spaces once at initialization, but we'd rather + * be paranoid about this. + */ +uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size); + +/** + * Perform one-time initializations. + * + * Create the NVRAM spaces, and set their initial values as needed. Sets the + * nvLocked bit and ensures the physical presence command is enabled and + * locked. + */ +uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware *rsf, + RollbackSpaceKernel *rsk); + +/** + * Start the TPM and establish the root of trust for the anti-rollback + * mechanism. + */ +uint32_t SetupTPM(int developer_mode, int disable_dev_request, + int clear_tpm_owner_request, RollbackSpaceFirmware *rsf); + +/** + * Utility function to turn the virtual dev-mode flag on or off. 0=off, 1=on. + */ +uint32_t SetVirtualDevMode(int val); + +#endif /* VBOOT_REFERENCE_ROLLBACK_INDEX_H_ */ diff --git a/src/include/tpm_lite/tlcl.h b/src/include/tpm_lite/tlcl.h new file mode 100644 index 0000000000..5373120066 --- /dev/null +++ b/src/include/tpm_lite/tlcl.h @@ -0,0 +1,209 @@ +/* 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. + */ + +/* + * TPM Lightweight Command Library. + * + * A low-level library for interfacing to TPM hardware or an emulator. + */ + +#ifndef TPM_LITE_TLCL_H_ +#define TPM_LITE_TLCL_H_ +#include <stdint.h> + +#include "tss_constants.h" + +/*****************************************************************************/ +/* Functions implemented in tlcl.c */ + +/** + * Call this first. Returns 0 if success, nonzero if error. + */ +uint32_t TlclLibInit(void); + +/** + * Call this on shutdown. Returns 0 if success, nonzero if error. + */ +uint32_t TlclLibClose(void); + +/* Low-level operations */ + +/** + * Perform a raw TPM request/response transaction. + */ +uint32_t TlclSendReceive(const uint8_t *request, uint8_t *response, + int max_length); + +/** + * Return the size of a TPM request or response packet. + */ +int TlclPacketSize(const uint8_t *packet); + +/* Commands */ + +/** + * Send a TPM_Startup(ST_CLEAR). The TPM error code is returned (0 for + * success). + */ +uint32_t TlclStartup(void); + +/** + * Save the TPM state. Normally done by the kernel before a suspend, included + * here for tests. The TPM error code is returned (0 for success). + */ +uint32_t TlclSaveState(void); + +/** + * Resume by sending a TPM_Startup(ST_STATE). The TPM error code is returned + * (0 for success). + */ +uint32_t TlclResume(void); + +/** + * Run the self test. + * + * Note---this is synchronous. To run this in parallel with other firmware, + * use ContinueSelfTest(). The TPM error code is returned. + */ +uint32_t TlclSelfTestFull(void); + +/** + * Run the self test in the background. + */ +uint32_t TlclContinueSelfTest(void); + +/** + * Define a space with permission [perm]. [index] is the index for the space, + * [size] the usable data size. The TPM error code is returned. + */ +uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size); + +/** + * Write [length] bytes of [data] to space at [index]. The TPM error code is + * returned. + */ +uint32_t TlclWrite(uint32_t index, const void *data, uint32_t length); + +/** + * Read [length] bytes from space at [index] into [data]. The TPM error code + * is returned. + */ +uint32_t TlclRead(uint32_t index, void *data, uint32_t length); + +/** + * Read PCR at [index] into [data]. [length] must be TPM_PCR_DIGEST or + * larger. The TPM error code is returned. + */ +uint32_t TlclPCRRead(uint32_t index, void *data, uint32_t length); + +/** + * Write-lock space at [index]. The TPM error code is returned. + */ +uint32_t TlclWriteLock(uint32_t index); + +/** + * Read-lock space at [index]. The TPM error code is returned. + */ +uint32_t TlclReadLock(uint32_t index); + +/** + * Assert physical presence in software. The TPM error code is returned. + */ +uint32_t TlclAssertPhysicalPresence(void); + +/** + * Enable the physical presence command. The TPM error code is returned. + */ +uint32_t TlclPhysicalPresenceCMDEnable(void); + +/** + * Finalize the physical presence settings: sofware PP is enabled, hardware PP + * is disabled, and the lifetime lock is set. The TPM error code is returned. + */ +uint32_t TlclFinalizePhysicalPresence(void); + +uint32_t TlclAssertPhysicalPresenceResult(void); + +/** + * Turn off physical presence and locks it off until next reboot. The TPM + * error code is returned. + */ +uint32_t TlclLockPhysicalPresence(void); + +/** + * Set the nvLocked bit. The TPM error code is returned. + */ +uint32_t TlclSetNvLocked(void); + +/** + * Return 1 if the TPM is owned, 0 otherwise. + */ +int TlclIsOwned(void); + +/** + * Issue a ForceClear. The TPM error code is returned. + */ +uint32_t TlclForceClear(void); + +/** + * Issue a PhysicalEnable. The TPM error code is returned. + */ +uint32_t TlclSetEnable(void); + +/** + * Issue a PhysicalDisable. The TPM error code is returned. + */ +uint32_t TlclClearEnable(void); + +/** + * Issue a SetDeactivated. Pass 0 to activate. Returns result code. + */ +uint32_t TlclSetDeactivated(uint8_t flag); + +/** + * Get flags of interest. Pointers for flags you aren't interested in may + * be NULL. The TPM error code is returned. + */ +uint32_t TlclGetFlags(uint8_t *disable, uint8_t *deactivated, + uint8_t *nvlocked); + +/** + * Set the bGlobalLock flag, which only a reboot can clear. The TPM error + * code is returned. + */ +uint32_t TlclSetGlobalLock(void); + +/** + * Perform a TPM_Extend. + */ +uint32_t TlclExtend(int pcr_num, const uint8_t *in_digest, uint8_t *out_digest); + +/** + * Get the permission bits for the NVRAM space with |index|. + */ +uint32_t TlclGetPermissions(uint32_t index, uint32_t *permissions); + +/** + * Get the entire set of permanent flags. + */ +uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS *pflags); + +/** + * Get the entire set of volatile (ST_CLEAR) flags. + */ +uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS *pflags); + +/** + * Get the ownership flag. The TPM error code is returned. + */ +uint32_t TlclGetOwnership(uint8_t *owned); + +/** + * Request [length] bytes from TPM RNG to be stored in [data]. Actual number of + * bytes read is stored in [size]. The TPM error code is returned. + */ +uint32_t TlclGetRandom(uint8_t *data, uint32_t length, uint32_t *size); + +#endif /* TPM_LITE_TLCL_H_ */ diff --git a/src/include/tpm_lite/tss_constants.h b/src/include/tpm_lite/tss_constants.h new file mode 100644 index 0000000000..883a5ad85e --- /dev/null +++ b/src/include/tpm_lite/tss_constants.h @@ -0,0 +1,96 @@ +/* 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. + * + * Some TPM constants and type definitions for standalone compilation for use + * in the firmware + */ +#ifndef VBOOT_REFERENCE_TSS_CONSTANTS_H_ +#define VBOOT_REFERENCE_TSS_CONSTANTS_H_ +#include <stdint.h> + +#define TPM_MAX_COMMAND_SIZE 4096 +#define TPM_LARGE_ENOUGH_COMMAND_SIZE 256 /* saves space in the firmware */ +#define TPM_PUBEK_SIZE 256 +#define TPM_PCR_DIGEST 20 + +#define TPM_E_NON_FATAL 0x800 + +#define TPM_SUCCESS ((uint32_t)0x00000000) + +#define TPM_E_AREA_LOCKED ((uint32_t)0x0000003c) +#define TPM_E_BADINDEX ((uint32_t)0x00000002) +#define TPM_E_BAD_PRESENCE ((uint32_t)0x0000002d) +#define TPM_E_IOERROR ((uint32_t)0x0000001f) +#define TPM_E_INVALID_POSTINIT ((uint32_t)0x00000026) +#define TPM_E_MAXNVWRITES ((uint32_t)0x00000048) +#define TPM_E_OWNER_SET ((uint32_t)0x00000014) + +#define TPM_E_NEEDS_SELFTEST ((uint32_t)(TPM_E_NON_FATAL + 1)) +#define TPM_E_DOING_SELFTEST ((uint32_t)(TPM_E_NON_FATAL + 2)) + +#define TPM_E_ALREADY_INITIALIZED ((uint32_t)0x00005000) /* vboot local */ +#define TPM_E_INTERNAL_INCONSISTENCY ((uint32_t)0x00005001) /* vboot local */ +#define TPM_E_MUST_REBOOT ((uint32_t)0x00005002) /* vboot local */ +#define TPM_E_CORRUPTED_STATE ((uint32_t)0x00005003) /* vboot local */ +#define TPM_E_COMMUNICATION_ERROR ((uint32_t)0x00005004) /* vboot local */ +#define TPM_E_RESPONSE_TOO_LARGE ((uint32_t)0x00005005) /* vboot local */ +#define TPM_E_NO_DEVICE ((uint32_t)0x00005006) /* vboot local */ +#define TPM_E_INPUT_TOO_SMALL ((uint32_t)0x00005007) /* vboot local */ +#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_NV_INDEX0 ((uint32_t)0x00000000) +#define TPM_NV_INDEX_LOCK ((uint32_t)0xffffffff) +#define TPM_NV_PER_GLOBALLOCK (((uint32_t)1)<<15) +#define TPM_NV_PER_PPWRITE (((uint32_t)1)<<0) +#define TPM_NV_PER_READ_STCLEAR (((uint32_t)1)<<31) +#define TPM_NV_PER_WRITE_STCLEAR (((uint32_t)1)<<14) + +#define TPM_TAG_RQU_COMMAND ((uint16_t) 0xc1) +#define TPM_TAG_RQU_AUTH1_COMMAND ((uint16_t) 0xc2) +#define TPM_TAG_RQU_AUTH2_COMMAND ((uint16_t) 0xc3) + +#define TPM_TAG_RSP_COMMAND ((uint16_t) 0xc4) +#define TPM_TAG_RSP_AUTH1_COMMAND ((uint16_t) 0xc5) +#define TPM_TAG_RSP_AUTH2_COMMAND ((uint16_t) 0xc6) + +typedef uint8_t TSS_BOOL; +typedef uint16_t TPM_STRUCTURE_TAG; + +typedef struct tdTPM_PERMANENT_FLAGS +{ + TPM_STRUCTURE_TAG tag; + TSS_BOOL disable; + TSS_BOOL ownership; + TSS_BOOL deactivated; + TSS_BOOL readPubek; + TSS_BOOL disableOwnerClear; + TSS_BOOL allowMaintenance; + TSS_BOOL physicalPresenceLifetimeLock; + TSS_BOOL physicalPresenceHWEnable; + TSS_BOOL physicalPresenceCMDEnable; + TSS_BOOL CEKPUsed; + TSS_BOOL TPMpost; + TSS_BOOL TPMpostLock; + TSS_BOOL FIPS; + TSS_BOOL Operator; + TSS_BOOL enableRevokeEK; + TSS_BOOL nvLocked; + TSS_BOOL readSRKPub; + TSS_BOOL tpmEstablished; + TSS_BOOL maintenanceDone; + TSS_BOOL disableFullDALogicInfo; +} TPM_PERMANENT_FLAGS; + +typedef struct tdTPM_STCLEAR_FLAGS{ + TPM_STRUCTURE_TAG tag; + TSS_BOOL deactivated; + TSS_BOOL disableForceClear; + TSS_BOOL physicalPresence; + TSS_BOOL physicalPresenceLock; + TSS_BOOL bGlobalLock; +} TPM_STCLEAR_FLAGS; + +#endif /* VBOOT_REFERENCE_TSS_CONSTANTS_H_ */ diff --git a/src/lib/tlcl.c b/src/lib/tlcl.c new file mode 100644 index 0000000000..bf2d27f99c --- /dev/null +++ b/src/lib/tlcl.c @@ -0,0 +1,466 @@ +/* Copyright (c) 2012 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. + */ + +/* A lightweight TPM command library. + * + * The general idea is that TPM commands are array of bytes whose + * fields are mostly compile-time constant. The goal is to build much + * of the commands at compile time (or build time) and change some of + * the fields at run time as needed. The code in + * utility/tlcl_generator.c builds structures containing the commands, + * as well as the offsets of the fields that need to be set at run + * time. + */ + +#include "sysincludes.h" + +#include "tlcl.h" +#include "tlcl_internal.h" +#include "tlcl_structures.h" +#include "utility.h" +#include "vboot_api.h" + +#ifdef FOR_TEST +/* Allow unit testing implementation of TlclSendReceive() */ +#undef CHROMEOS_ENVIRONMENT +#endif + +/* Sets the size field of a TPM command. */ +static inline void SetTpmCommandSize(uint8_t* buffer, uint32_t size) { + ToTpmUint32(buffer + sizeof(uint16_t), size); +} + +/* Gets the size field of a TPM command. */ +__attribute__((unused)) +static inline int TpmCommandSize(const uint8_t* buffer) { + uint32_t size; + FromTpmUint32(buffer + sizeof(uint16_t), &size); + return (int) size; +} + +/* Gets the size field of a TPM request or response. */ +int TlclPacketSize(const uint8_t* packet) { + return TpmCommandSize(packet); +} + +/* Gets the code field of a TPM command. */ +static inline int TpmCommandCode(const uint8_t* buffer) { + uint32_t code; + FromTpmUint32(buffer + sizeof(uint16_t) + sizeof(uint32_t), &code); + return code; +} + +/* Gets the return code field of a TPM result. */ +static inline int TpmReturnCode(const uint8_t* buffer) { + return TpmCommandCode(buffer); +} + +/* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or + * DOING_SELFTEST errors are returned. + */ +static uint32_t TlclSendReceiveNoRetry(const uint8_t* request, + uint8_t* response, int max_length) { + + uint32_t response_length = max_length; + uint32_t result; + +#ifdef EXTRA_LOGGING + VBDEBUG(("TPM: command: %x%x %x%x%x%x %x%x%x%x\n", + request[0], request[1], + request[2], request[3], request[4], request[5], + request[6], request[7], request[8], request[9])); +#endif + + result = VbExTpmSendReceive(request, TpmCommandSize(request), + response, &response_length); + if (0 != result) { + /* Communication with TPM failed, so response is garbage */ + VBDEBUG(("TPM: command 0x%x send/receive failed: 0x%x\n", + TpmCommandCode(request), result)); + return result; + } + /* Otherwise, use the result code from the response */ + result = TpmReturnCode(response); + + /* TODO: add paranoia about returned response_length vs. max_length + * (and possibly expected length from the response header). See + * crosbug.com/17017 */ + +#ifdef EXTRA_LOGGING + VBDEBUG(("TPM: response: %x%x %x%x%x%x %x%x%x%x\n", + response[0], response[1], + response[2], response[3], response[4], response[5], + response[6], response[7], response[8], response[9])); +#endif + + VBDEBUG(("TPM: command 0x%x returned 0x%x\n", + TpmCommandCode(request), result)); + + return result; +} + + +/* Sends a TPM command and gets a response. Returns 0 if success or the TPM + * error code if error. In the firmware, waits for the self test to complete + * if needed. In the host, reports the first error without retries. */ +uint32_t TlclSendReceive(const uint8_t* request, uint8_t* response, + int max_length) { + uint32_t result = TlclSendReceiveNoRetry(request, response, max_length); + /* When compiling for the firmware, hide command failures due to the self + * test not having run or completed. */ +#ifndef CHROMEOS_ENVIRONMENT + /* If the command fails because the self test has not completed, try it + * again after attempting to ensure that the self test has completed. */ + if (result == TPM_E_NEEDS_SELFTEST || result == TPM_E_DOING_SELFTEST) { + result = TlclContinueSelfTest(); + if (result != TPM_SUCCESS) { + return result; + } +#if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE) + /* Retry only once */ + result = TlclSendReceiveNoRetry(request, response, max_length); +#else + /* This needs serious testing. The TPM specification says: "iii. The + * caller MUST wait for the actions of TPM_ContinueSelfTest to complete + * before reissuing the command C1." But, if ContinueSelfTest is + * non-blocking, how do we know that the actions have completed other than + * trying again? */ + do { + result = TlclSendReceiveNoRetry(request, response, max_length); + } while (result == TPM_E_DOING_SELFTEST); +#endif + } +#endif /* ! defined(CHROMEOS_ENVIRONMENT) */ + return result; +} + +/* Sends a command and returns the error code. */ +static uint32_t Send(const uint8_t* command) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + return TlclSendReceive(command, response, sizeof(response)); +} + +/* Exported functions. */ + +uint32_t TlclLibInit(void) { + return VbExTpmInit(); +} + +uint32_t TlclLibClose(void) { + return VbExTpmClose(); +} + +uint32_t TlclStartup(void) { + VBDEBUG(("TPM: Startup\n")); + return Send(tpm_startup_cmd.buffer); +} + +uint32_t TlclSaveState(void) { + VBDEBUG(("TPM: SaveState\n")); + return Send(tpm_savestate_cmd.buffer); +} + +uint32_t TlclResume(void) { + VBDEBUG(("TPM: Resume\n")); + return Send(tpm_resume_cmd.buffer); +} + +uint32_t TlclSelfTestFull(void) { + VBDEBUG(("TPM: Self test full\n")); + return Send(tpm_selftestfull_cmd.buffer); +} + +uint32_t TlclContinueSelfTest(void) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + VBDEBUG(("TPM: Continue self test\n")); + /* Call the No Retry version of SendReceive to avoid recursion. */ + return TlclSendReceiveNoRetry(tpm_continueselftest_cmd.buffer, + response, sizeof(response)); +} + +uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { + struct s_tpm_nv_definespace_cmd cmd; + VBDEBUG(("TPM: TlclDefineSpace(0x%x, 0x%x, %d)\n", index, perm, size)); + Memcpy(&cmd, &tpm_nv_definespace_cmd, sizeof(cmd)); + ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.index, index); + ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.perm, perm); + ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.size, size); + return Send(cmd.buffer); +} + +uint32_t TlclWrite(uint32_t index, const void* data, uint32_t length) { + struct s_tpm_nv_write_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + const int total_length = + kTpmRequestHeaderLength + kWriteInfoLength + length; + + VBDEBUG(("TPM: TlclWrite(0x%x, %d)\n", index, length)); + Memcpy(&cmd, &tpm_nv_write_cmd, sizeof(cmd)); + VbAssert(total_length <= TPM_LARGE_ENOUGH_COMMAND_SIZE); + SetTpmCommandSize(cmd.buffer, total_length); + + ToTpmUint32(cmd.buffer + tpm_nv_write_cmd.index, index); + ToTpmUint32(cmd.buffer + tpm_nv_write_cmd.length, length); + Memcpy(cmd.buffer + tpm_nv_write_cmd.data, data, length); + + return TlclSendReceive(cmd.buffer, response, sizeof(response)); +} + +uint32_t TlclRead(uint32_t index, void* data, uint32_t length) { + struct s_tpm_nv_read_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t result_length; + uint32_t result; + + VBDEBUG(("TPM: TlclRead(0x%x, %d)\n", index, length)); + Memcpy(&cmd, &tpm_nv_read_cmd, sizeof(cmd)); + ToTpmUint32(cmd.buffer + tpm_nv_read_cmd.index, index); + ToTpmUint32(cmd.buffer + tpm_nv_read_cmd.length, length); + + result = TlclSendReceive(cmd.buffer, response, sizeof(response)); + if (result == TPM_SUCCESS && length > 0) { + uint8_t* nv_read_cursor = response + kTpmResponseHeaderLength; + FromTpmUint32(nv_read_cursor, &result_length); + nv_read_cursor += sizeof(uint32_t); + Memcpy(data, nv_read_cursor, result_length); + } + + return result; +} + +uint32_t TlclPCRRead(uint32_t index, void* data, uint32_t length) { + struct s_tpm_pcr_read_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t result; + + VBDEBUG(("TPM: TlclPCRRead(0x%x, %d)\n", index, length)); + if (length < kPcrDigestLength) { + return TPM_E_IOERROR; + } + Memcpy(&cmd, &tpm_pcr_read_cmd, sizeof(cmd)); + ToTpmUint32(cmd.buffer + tpm_pcr_read_cmd.pcrNum, index); + + result = TlclSendReceive(cmd.buffer, response, sizeof(response)); + if (result == TPM_SUCCESS) { + uint8_t* pcr_read_cursor = response + kTpmResponseHeaderLength; + Memcpy(data, pcr_read_cursor, kPcrDigestLength); + } + + return result; +} + +uint32_t TlclWriteLock(uint32_t index) { + VBDEBUG(("TPM: Write lock 0x%x\n", index)); + return TlclWrite(index, NULL, 0); +} + +uint32_t TlclReadLock(uint32_t index) { + VBDEBUG(("TPM: Read lock 0x%x\n", index)); + return TlclRead(index, NULL, 0); +} + +uint32_t TlclAssertPhysicalPresence(void) { + VBDEBUG(("TPM: Asserting physical presence\n")); + return Send(tpm_ppassert_cmd.buffer); +} + +uint32_t TlclPhysicalPresenceCMDEnable(void) { + VBDEBUG(("TPM: Enable the physical presence command\n")); + return Send(tpm_ppenable_cmd.buffer); +} + +uint32_t TlclFinalizePhysicalPresence(void) { + VBDEBUG(("TPM: Enable PP cmd, disable HW pp, and set lifetime lock\n")); + return Send(tpm_finalizepp_cmd.buffer); +} + +uint32_t TlclAssertPhysicalPresenceResult(void) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + return TlclSendReceive(tpm_ppassert_cmd.buffer, response, sizeof(response)); +} + +uint32_t TlclLockPhysicalPresence(void) { + VBDEBUG(("TPM: Lock physical presence\n")); + return Send(tpm_pplock_cmd.buffer); +} + +uint32_t TlclSetNvLocked(void) { + VBDEBUG(("TPM: Set NV locked\n")); + return TlclDefineSpace(TPM_NV_INDEX_LOCK, 0, 0); +} + +int TlclIsOwned(void) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE + TPM_PUBEK_SIZE]; + uint32_t result; + result = TlclSendReceive(tpm_readpubek_cmd.buffer, + response, sizeof(response)); + return (result != TPM_SUCCESS); +} + +uint32_t TlclForceClear(void) { + VBDEBUG(("TPM: Force clear\n")); + return Send(tpm_forceclear_cmd.buffer); +} + +uint32_t TlclSetEnable(void) { + VBDEBUG(("TPM: Enabling TPM\n")); + return Send(tpm_physicalenable_cmd.buffer); +} + +uint32_t TlclClearEnable(void) { + VBDEBUG(("TPM: Disabling TPM\n")); + return Send(tpm_physicaldisable_cmd.buffer); +} + +uint32_t TlclSetDeactivated(uint8_t flag) { + struct s_tpm_physicalsetdeactivated_cmd cmd; + VBDEBUG(("TPM: SetDeactivated(%d)\n", flag)); + Memcpy(&cmd, &tpm_physicalsetdeactivated_cmd, sizeof(cmd)); + *(cmd.buffer + cmd.deactivated) = flag; + return Send(cmd.buffer); +} + +uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS* pflags) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t size; + uint32_t result = + TlclSendReceive(tpm_getflags_cmd.buffer, response, sizeof(response)); + if (result != TPM_SUCCESS) + return result; + FromTpmUint32(response + kTpmResponseHeaderLength, &size); + /* TODO(crbug.com/379255): This fails. Find out why. + * VbAssert(size == sizeof(TPM_PERMANENT_FLAGS)); + */ + Memcpy(pflags, + response + kTpmResponseHeaderLength + sizeof(size), + sizeof(TPM_PERMANENT_FLAGS)); + return result; +} + +uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS* vflags) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t size; + uint32_t result = + TlclSendReceive(tpm_getstclearflags_cmd.buffer, response, sizeof(response)); + if (result != TPM_SUCCESS) + return result; + FromTpmUint32(response + kTpmResponseHeaderLength, &size); + /* Ugly assertion, but the struct is padded up by one byte. */ + /* TODO(crbug.com/379255): This fails. Find out why. + * VbAssert(size == 7 && sizeof(TPM_STCLEAR_FLAGS) - 1 == 7); + */ + Memcpy(vflags, + response + kTpmResponseHeaderLength + sizeof(size), + sizeof(TPM_STCLEAR_FLAGS)); + return result; +} + +uint32_t TlclGetFlags(uint8_t* disable, + uint8_t* deactivated, + uint8_t *nvlocked) { + TPM_PERMANENT_FLAGS pflags; + uint32_t result = TlclGetPermanentFlags(&pflags); + if (result == TPM_SUCCESS) { + if (disable) + *disable = pflags.disable; + if (deactivated) + *deactivated = pflags.deactivated; + if (nvlocked) + *nvlocked = pflags.nvLocked; + VBDEBUG(("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n", + pflags.disable, pflags.deactivated, pflags.nvLocked)); + } + return result; +} + +uint32_t TlclSetGlobalLock(void) { + uint32_t x; + VBDEBUG(("TPM: Set global lock\n")); + return TlclWrite(TPM_NV_INDEX0, (uint8_t*) &x, 0); +} + +uint32_t TlclExtend(int pcr_num, const uint8_t* in_digest, + uint8_t* out_digest) { + struct s_tpm_extend_cmd cmd; + uint8_t response[kTpmResponseHeaderLength + kPcrDigestLength]; + uint32_t result; + + Memcpy(&cmd, &tpm_extend_cmd, sizeof(cmd)); + ToTpmUint32(cmd.buffer + tpm_extend_cmd.pcrNum, pcr_num); + Memcpy(cmd.buffer + cmd.inDigest, in_digest, kPcrDigestLength); + + result = TlclSendReceive(cmd.buffer, response, sizeof(response)); + if (result != TPM_SUCCESS) + return result; + + Memcpy(out_digest, response + kTpmResponseHeaderLength, kPcrDigestLength); + return result; +} + +uint32_t TlclGetPermissions(uint32_t index, uint32_t* permissions) { + struct s_tpm_getpermissions_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint8_t* nvdata; + uint32_t result; + uint32_t size; + + Memcpy(&cmd, &tpm_getpermissions_cmd, sizeof(cmd)); + ToTpmUint32(cmd.buffer + tpm_getpermissions_cmd.index, index); + result = TlclSendReceive(cmd.buffer, response, sizeof(response)); + if (result != TPM_SUCCESS) + return result; + + nvdata = response + kTpmResponseHeaderLength + sizeof(size); + FromTpmUint32(nvdata + kNvDataPublicPermissionsOffset, permissions); + return result; +} + +uint32_t TlclGetOwnership(uint8_t* owned) { + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t size; + uint32_t result = + TlclSendReceive(tpm_getownership_cmd.buffer, response, sizeof(response)); + if (result != TPM_SUCCESS) + return result; + FromTpmUint32(response + kTpmResponseHeaderLength, &size); + /* TODO(crbug.com/379255): This fails. Find out why. + * VbAssert(size == sizeof(*owned)); + */ + Memcpy(owned, + response + kTpmResponseHeaderLength + sizeof(size), + sizeof(*owned)); + return result; +} + +uint32_t TlclGetRandom(uint8_t* data, uint32_t length, uint32_t *size) { + struct s_tpm_get_random_cmd cmd; + uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + uint32_t result; + + VBDEBUG(("TPM: TlclGetRandom(%d)\n", length)); + Memcpy(&cmd, &tpm_get_random_cmd, sizeof(cmd)); + ToTpmUint32(cmd.buffer + tpm_get_random_cmd.bytesRequested, length); + /* There must be room in the response buffer for the bytes. */ + if (length > TPM_LARGE_ENOUGH_COMMAND_SIZE - kTpmResponseHeaderLength + - sizeof(uint32_t)) { + return TPM_E_IOERROR; + } + + result = TlclSendReceive(cmd.buffer, response, sizeof(response)); + if (result == TPM_SUCCESS) { + uint8_t* get_random_cursor; + FromTpmUint32(response + kTpmResponseHeaderLength, size); + + /* There must be room in the target buffer for the bytes. */ + if (*size > length) { + return TPM_E_RESPONSE_TOO_LARGE; + } + get_random_cursor = response + kTpmResponseHeaderLength + + sizeof(uint32_t); + Memcpy(data, get_random_cursor, *size); + } + + return result; +} diff --git a/src/lib/tlcl_internal.h b/src/lib/tlcl_internal.h new file mode 100644 index 0000000000..51fe6ef3f0 --- /dev/null +++ b/src/lib/tlcl_internal.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2010 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. + */ + +#ifndef TPM_LITE_TLCL_INTERNAL_H_ +#define TPM_LITE_TLCL_INTERNAL_H_ + +/* + * These numbers derive from adding the sizes of command fields as shown in the + * TPM commands manual. + */ +#define kTpmRequestHeaderLength 10 +#define kTpmResponseHeaderLength 10 +#define kTpmReadInfoLength 12 +#define kEncAuthLength 20 +#define kPcrDigestLength 20 + + +/* + * Conversion functions. ToTpmTYPE puts a value of type TYPE into a TPM + * command buffer. FromTpmTYPE gets a value of type TYPE from a TPM command + * buffer into a variable. + */ +__attribute__((unused)) +static inline void ToTpmUint32(uint8_t *buffer, uint32_t x) { + buffer[0] = (uint8_t)(x >> 24); + buffer[1] = (uint8_t)((x >> 16) & 0xff); + buffer[2] = (uint8_t)((x >> 8) & 0xff); + buffer[3] = (uint8_t)(x & 0xff); +} + +/* + * See comment for above function. + */ +__attribute__((unused)) +static inline void FromTpmUint32(const uint8_t *buffer, uint32_t *x) { + *x = ((buffer[0] << 24) | + (buffer[1] << 16) | + (buffer[2] << 8) | + buffer[3]); +} + +/* + * See comment for above function. + */ +__attribute__((unused)) +static inline void ToTpmUint16(uint8_t *buffer, uint16_t x) { + buffer[0] = (uint8_t)(x >> 8); + buffer[1] = (uint8_t)(x & 0xff); +} + +/* + * See comment for above function. + */ +__attribute__((unused)) +static inline void FromTpmUint16(const uint8_t *buffer, uint16_t *x) { + *x = (buffer[0] << 8) | buffer[1]; +} + +#endif /* TPM_LITE_TLCL_INTERNAL_H_ */ diff --git a/src/lib/tlcl_structures.h b/src/lib/tlcl_structures.h new file mode 100644 index 0000000000..36c1bb9ed8 --- /dev/null +++ b/src/lib/tlcl_structures.h @@ -0,0 +1,138 @@ +/* This file is automatically generated */ + +const struct s_tpm_extend_cmd{ + uint8_t buffer[34]; + uint16_t pcrNum; + uint16_t inDigest; +} tpm_extend_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x14, }, +10, 14, }; + +const struct s_tpm_get_random_cmd{ + uint8_t buffer[14]; + uint16_t bytesRequested; +} tpm_get_random_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x46, }, +10, }; + +const struct s_tpm_getownership_cmd{ + uint8_t buffer[22]; +} tpm_getownership_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x11, }, +}; + +const struct s_tpm_getpermissions_cmd{ + uint8_t buffer[22]; + uint16_t index; +} tpm_getpermissions_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x11, 0x0, 0x0, 0x0, 0x4, }, +18, }; + +const struct s_tpm_getstclearflags_cmd{ + uint8_t buffer[22]; +} tpm_getstclearflags_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x9, }, +}; + +const struct s_tpm_getflags_cmd{ + uint8_t buffer[22]; +} tpm_getflags_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x1, 0x8, }, +}; + +const struct s_tpm_physicalsetdeactivated_cmd{ + uint8_t buffer[11]; + uint16_t deactivated; +} tpm_physicalsetdeactivated_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x72, }, +10, }; + +const struct s_tpm_physicalenable_cmd{ + uint8_t buffer[10]; +} tpm_physicalenable_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x6f, }, +}; + +const struct s_tpm_physicaldisable_cmd{ + uint8_t buffer[10]; +} tpm_physicaldisable_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x70, }, +}; + +const struct s_tpm_forceclear_cmd{ + uint8_t buffer[10]; +} tpm_forceclear_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x5d, }, +}; + +const struct s_tpm_readpubek_cmd{ + uint8_t buffer[30]; +} tpm_readpubek_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x7c, }, +}; + +const struct s_tpm_continueselftest_cmd{ + uint8_t buffer[10]; +} tpm_continueselftest_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53, }, +}; + +const struct s_tpm_selftestfull_cmd{ + uint8_t buffer[10]; +} tpm_selftestfull_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x50, }, +}; + +const struct s_tpm_resume_cmd{ + uint8_t buffer[12]; +} tpm_resume_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x2, }, +}; + +const struct s_tpm_savestate_cmd{ + uint8_t buffer[10]; +} tpm_savestate_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x98, }, +}; + +const struct s_tpm_startup_cmd{ + uint8_t buffer[12]; +} tpm_startup_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x1, }, +}; + +const struct s_tpm_finalizepp_cmd{ + uint8_t buffer[12]; +} tpm_finalizepp_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x2, 0xa0, }, +}; + +const struct s_tpm_pplock_cmd{ + uint8_t buffer[12]; +} tpm_pplock_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x4, }, +}; + +const struct s_tpm_ppenable_cmd{ + uint8_t buffer[12]; +} tpm_ppenable_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x20, }, +}; + +const struct s_tpm_ppassert_cmd{ + uint8_t buffer[12]; +} tpm_ppassert_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x40, 0x0, 0x0, 0xa, 0x0, 0x8, }, +}; + +const struct s_tpm_pcr_read_cmd{ + uint8_t buffer[14]; + uint16_t pcrNum; +} tpm_pcr_read_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x15, }, +10, }; + +const struct s_tpm_nv_read_cmd{ + uint8_t buffer[22]; + uint16_t index; + uint16_t length; +} tpm_nv_read_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0xcf, }, +10, 18, }; + +const struct s_tpm_nv_write_cmd{ + uint8_t buffer[256]; + uint16_t index; + uint16_t length; + uint16_t data; +} tpm_nv_write_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd, }, +10, 18, 22, }; + +const struct s_tpm_nv_definespace_cmd{ + uint8_t buffer[101]; + uint16_t index; + uint16_t perm; + uint16_t size; +} tpm_nv_definespace_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0x65, 0x0, 0x0, 0x0, 0xcc, 0x0, 0x18, 0, 0, 0, 0, 0x0, 0x3, 0, 0, 0, 0x1f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x3, 0, 0, 0, 0x1f, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x0, 0x17, }, +12, 70, 77, }; + +const int kWriteInfoLength = 12; +const int kNvDataPublicPermissionsOffset = 60; diff --git a/src/lib/tpm_error_messages.h b/src/lib/tpm_error_messages.h new file mode 100644 index 0000000000..14cb86beae --- /dev/null +++ b/src/lib/tpm_error_messages.h @@ -0,0 +1,250 @@ +/* Copyright (c) 2010 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.
+ */
+
+/* TPM error codes.
+ *
+ * Copy-pasted and lightly edited from TCG TPM Main Part 2 TPM Structures
+ * Version 1.2 Level 2 Revision 103 26 October 2006 Draft.
+ */
+
+#ifndef TPM_ERROR_MESSAGES_H
+#define TPM_ERROR_MESSAGES_H
+
+#define TPM_E_BASE 0x0
+#define TPM_E_NON_FATAL 0x800
+
+typedef struct tpm_error_info {
+ const char* name;
+ uint32_t code;
+ const char* description;
+} tpm_error_info;
+
+tpm_error_info tpm_error_table[] = {
+{ "TPM_AUTHFAIL", TPM_E_BASE + 1,
+"Authentication failed" },
+{ "TPM_BADINDEX", TPM_E_BASE + 2,
+"The index to a PCR, DIR or other register is incorrect" },
+{ "TPM_BAD_PARAMETER", TPM_E_BASE + 3,
+"One or more parameter is bad" },
+{ "TPM_AUDITFAILURE", TPM_E_BASE + 4,
+"An operation completed successfully\n\
+but the auditing of that operation failed" },
+{ "TPM_CLEAR_DISABLED", TPM_E_BASE + 5,
+"The clear disable flag is set and all clear operations now require\n\
+physical access" },
+{ "TPM_DEACTIVATED", TPM_E_BASE + 6,
+"The TPM is deactivated" },
+{ "TPM_DISABLED", TPM_E_BASE + 7,
+"The TPM is disabled" },
+{ "TPM_DISABLED_CMD", TPM_E_BASE + 8,
+"The target command has been disabled" },
+{ "TPM_FAIL", TPM_E_BASE + 9,
+"The operation failed" },
+{ "TPM_BAD_ORDINAL", TPM_E_BASE + 10,
+"The ordinal was unknown or inconsistent" },
+{ "TPM_INSTALL_DISABLED", TPM_E_BASE + 11,
+"The ability to install an owner is disabled" },
+{ "TPM_INVALID_KEYHANDLE", TPM_E_BASE + 12,
+"The key handle can not be interpreted" },
+{ "TPM_KEYNOTFOUND", TPM_E_BASE + 13,
+"The key handle points to an invalid key" },
+{ "TPM_INAPPROPRIATE_ENC", TPM_E_BASE + 14,
+"Unacceptable encryption scheme" },
+{ "TPM_MIGRATEFAIL", TPM_E_BASE + 15,
+"Migration authorization failed" },
+{ "TPM_INVALID_PCR_INFO", TPM_E_BASE + 16,
+"PCR information could not be interpreted" },
+{ "TPM_NOSPACE", TPM_E_BASE + 17,
+"No room to load key" },
+{ "TPM_NOSRK", TPM_E_BASE + 18,
+"There is no SRK set" },
+{ "TPM_NOTSEALED_BLOB", TPM_E_BASE + 19,
+"An encrypted blob is invalid or was not created by this TPM" },
+{ "TPM_OWNER_SET", TPM_E_BASE + 20,
+"There is already an Owner" },
+{ "TPM_RESOURCES", TPM_E_BASE + 21,
+"The TPM has insufficient internal resources to perform the requested action" },
+{ "TPM_SHORTRANDOM", TPM_E_BASE + 22,
+"A random string was too short" },
+{ "TPM_SIZE", TPM_E_BASE + 23,
+"The TPM does not have the space to perform the operation" },
+{ "TPM_WRONGPCRVAL", TPM_E_BASE + 24,
+"The named PCR value does not match the current PCR value" },
+{ "TPM_BAD_PARAM_SIZE", TPM_E_BASE + 25,
+"The paramSize argument to the command has the incorrect value" },
+{ "TPM_SHA_THREAD", TPM_E_BASE + 26,
+"There is no existing SHA-1 thread" },
+{ "TPM_SHA_ERROR", TPM_E_BASE + 27,
+"The calculation is unable to proceed because the existing SHA-1\n\
+thread has already encountered an error" },
+{ "TPM_FAILEDSELFTEST", TPM_E_BASE + 28,
+"Self-test has failed and the TPM has shutdown" },
+{ "TPM_AUTH2FAIL", TPM_E_BASE + 29,
+"The authorization for the second key in a 2 key function\n\
+failed authorization" },
+{ "TPM_BADTAG", TPM_E_BASE + 30,
+"The tag value sent to for a command is invalid" },
+{ "TPM_IOERROR", TPM_E_BASE + 31,
+"An IO error occurred transmitting information to the TPM" },
+{ "TPM_ENCRYPT_ERROR", TPM_E_BASE + 32,
+"The encryption process had a problem" },
+{ "TPM_DECRYPT_ERROR", TPM_E_BASE + 33,
+"The decryption process did not complete" },
+{ "TPM_INVALID_AUTHHANDLE", TPM_E_BASE + 34,
+"An invalid handle was used" },
+{ "TPM_NO_ENDORSEMENT", TPM_E_BASE + 35,
+"The TPM does not a EK installed" },
+{ "TPM_INVALID_KEYUSAGE", TPM_E_BASE + 36,
+"The usage of a key is not allowed" },
+{ "TPM_WRONG_ENTITYTYPE", TPM_E_BASE + 37,
+"The submitted entity type is not allowed" },
+{ "TPM_INVALID_POSTINIT", TPM_E_BASE + 38,
+"The command was received in the wrong sequence relative to TPM_Init\n\
+and a subsequent TPM_Startup" },
+{ "TPM_INAPPROPRIATE_SIG", TPM_E_BASE + 39,
+"Signed data cannot include additional DER information" },
+{ "TPM_BAD_KEY_PROPERTY", TPM_E_BASE + 40,
+"The key properties in TPM_KEY_PARMs are not supported by this TPM" },
+{ "TPM_BAD_MIGRATION", TPM_E_BASE + 41,
+"The migration properties of this key are incorrect" },
+{ "TPM_BAD_SCHEME", TPM_E_BASE + 42,
+"The signature or encryption scheme for this key is incorrect or not\n\
+permitted in this situation" },
+{ "TPM_BAD_DATASIZE", TPM_E_BASE + 43,
+"The size of the data (or blob) parameter is bad or inconsistent\n\
+with the referenced key" },
+{ "TPM_BAD_MODE", TPM_E_BASE + 44,
+"A mode parameter is bad, such as capArea or subCapArea for\n\
+TPM_GetCapability, physicalPresence parameter for TPM_PhysicalPresence,\n\
+or migrationType for, TPM_CreateMigrationBlob" },
+{ "TPM_BAD_PRESENCE", TPM_E_BASE + 45,
+"Either the physicalPresence or physicalPresenceLock bits\n\
+have the wrong value" },
+{ "TPM_BAD_VERSION", TPM_E_BASE + 46,
+"The TPM cannot perform this version of the capability" },
+{ "TPM_NO_WRAP_TRANSPORT", TPM_E_BASE + 47,
+"The TPM does not allow for wrapped transport sessions" },
+{ "TPM_AUDITFAIL_UNSUCCESSFUL", TPM_E_BASE + 48,
+"TPM audit construction failed and the underlying command\n\
+was returning a failure code also" },
+{ "TPM_AUDITFAIL_SUCCESSFUL", TPM_E_BASE + 49,
+"TPM audit construction failed and the underlying command\n\
+was returning success" },
+{ "TPM_NOTRESETABLE", TPM_E_BASE + 50,
+"Attempt to reset a PCR register that does not have the resettable attribute" },
+{ "TPM_NOTLOCAL", TPM_E_BASE + 51,
+"Attempt to reset a PCR register that requires locality\n\
+and locality modifier not part of command transport" },
+{ "TPM_BAD_TYPE", TPM_E_BASE + 52,
+"Make identity blob not properly typed" },
+{ "TPM_INVALID_RESOURCE", TPM_E_BASE + 53,
+"When saving context identified resource type does not match actual resource" },
+{ "TPM_NOTFIPS", TPM_E_BASE + 54,
+"The TPM is attempting to execute a command only available when in FIPS mode" },
+{ "TPM_INVALID_FAMILY", TPM_E_BASE + 55,
+"The command is attempting to use an invalid family ID" },
+{ "TPM_NO_NV_PERMISSION", TPM_E_BASE + 56,
+"The permission to manipulate the NV storage is not available" },
+{ "TPM_REQUIRES_SIGN", TPM_E_BASE + 57,
+"The operation requires a signed command" },
+{ "TPM_KEY_NOTSUPPORTED", TPM_E_BASE + 58,
+"Wrong operation to load an NV key" },
+{ "TPM_AUTH_CONFLICT", TPM_E_BASE + 59,
+"NV_LoadKey blob requires both owner and blob authorization" },
+{ "TPM_AREA_LOCKED", TPM_E_BASE + 60,
+"The NV area is locked and not writable" },
+{ "TPM_BAD_LOCALITY", TPM_E_BASE + 61,
+"The locality is incorrect for the attempted operation" },
+{ "TPM_READ_ONLY", TPM_E_BASE + 62,
+"The NV area is read only and canât be written to" },
+{ "TPM_PER_NOWRITE", TPM_E_BASE + 63,
+"There is no protection on the write to the NV area" },
+{ "TPM_FAMILYCOUNT", TPM_E_BASE + 64,
+"The family count value does not match" },
+{ "TPM_WRITE_LOCKED", TPM_E_BASE + 65,
+"The NV area has already been written to" },
+{ "TPM_BAD_ATTRIBUTES", TPM_E_BASE + 66,
+"The NV area attributes conflict" },
+{ "TPM_INVALID_STRUCTURE", TPM_E_BASE + 67,
+"The structure tag and version are invalid or inconsistent" },
+{ "TPM_KEY_OWNER_CONTROL", TPM_E_BASE + 68,
+"The key is under control of the TPM Owner and can only be evicted\n\
+by the TPM Owner" },
+{ "TPM_BAD_COUNTER", TPM_E_BASE + 69,
+"The counter handle is incorrect" },
+{ "TPM_NOT_FULLWRITE", TPM_E_BASE + 70,
+"The write is not a complete write of the area" },
+{ "TPM_CONTEXT_GAP", TPM_E_BASE + 71,
+"The gap between saved context counts is too large" },
+{ "TPM_MAXNVWRITES", TPM_E_BASE + 72,
+"The maximum number of NV writes without an owner has been exceeded" },
+{ "TPM_NOOPERATOR", TPM_E_BASE + 73,
+"No operator AuthData value is set" },
+{ "TPM_RESOURCEMISSING", TPM_E_BASE + 74,
+"The resource pointed to by context is not loaded" },
+{ "TPM_DELEGATE_LOCK", TPM_E_BASE + 75,
+"The delegate administration is locked" },
+{ "TPM_DELEGATE_FAMILY", TPM_E_BASE + 76,
+"Attempt to manage a family other then the delegated family" },
+{ "TPM_DELEGATE_ADMIN", TPM_E_BASE + 77,
+"Delegation table management not enabled" },
+{ "TPM_TRANSPORT_NOTEXCLUSIVE", TPM_E_BASE + 78,
+"There was a command executed outside of an exclusive transport session" },
+{ "TPM_OWNER_CONTROL", TPM_E_BASE + 79,
+"Attempt to context save a owner evict controlled key" },
+{ "TPM_DAA_RESOURCES", TPM_E_BASE + 80,
+"The DAA command has no resources available to execute the command" },
+{ "TPM_DAA_INPUT_DATA0", TPM_E_BASE + 81,
+"The consistency check on DAA parameter inputData0 has failed" },
+{ "TPM_DAA_INPUT_DATA1", TPM_E_BASE + 82,
+"The consistency check on DAA parameter inputData1 has failed" },
+{ "TPM_DAA_ISSUER_SETTINGS", TPM_E_BASE + 83,
+"The consistency check on DAA_issuerSettings has failed" },
+{ "TPM_DAA_TPM_SETTINGS", TPM_E_BASE + 84,
+"The consistency check on DAA_tpmSpecific has failed" },
+{ "TPM_DAA_STAGE", TPM_E_BASE + 85,
+"The atomic process indicated by the submitted DAA command is not\n\
+the expected process" },
+{ "TPM_DAA_ISSUER_VALIDITY", TPM_E_BASE + 86,
+"The issuerâs validity check has detected an inconsistency" },
+{ "TPM_DAA_WRONG_W", TPM_E_BASE + 87,
+"The consistency check on w has failed" },
+{ "TPM_BAD_HANDLE", TPM_E_BASE + 88,
+"The handle is incorrect" },
+{ "TPM_BAD_DELEGATE", TPM_E_BASE + 89,
+"Delegation is not correct" },
+{ "TPM_BADCONTEXT", TPM_E_BASE + 90,
+"The context blob is invalid" },
+{ "TPM_TOOMANYCONTEXTS", TPM_E_BASE + 91,
+"Too many contexts held by the TPM" },
+{ "TPM_MA_TICKET_SIGNATURE", TPM_E_BASE + 92,
+"Migration authority signature validation failure" },
+{ "TPM_MA_DESTINATION", TPM_E_BASE + 93,
+"Migration destination not authenticated" },
+{ "TPM_MA_SOURCE", TPM_E_BASE + 94,
+"Migration source incorrect" },
+{ "TPM_MA_AUTHORITY", TPM_E_BASE + 95,
+"Incorrect migration authority" },
+{ "TPM_PERMANENTEK", TPM_E_BASE + 97,
+"Attempt to revoke the EK and the EK is not revocable" },
+{ "TPM_BAD_SIGNATURE", TPM_E_BASE + 98,
+"Bad signature of CMK ticket" },
+{ "TPM_NOCONTEXTSPACE", TPM_E_BASE + 99,
+"There is no room in the context list for additional contexts" },
+{ "TPM_RETRY", TPM_E_BASE + TPM_E_NON_FATAL,
+"The TPM is too busy to respond to the command immediately, but\n\
+the command could be resubmitted at a later time. The TPM MAY\n\
+return TPM_RETRY for any command at any time" },
+{ "TPM_NEEDS_SELFTEST", TPM_E_BASE + TPM_E_NON_FATAL + 1,
+"TPM_ContinueSelfTest has not been run" },
+{ "TPM_DOING_SELFTEST", TPM_E_BASE + TPM_E_NON_FATAL + 2,
+"The TPM is currently executing the actions of TPM_ContinueSelfTest\n\
+because the ordinal required resources that have not been tested" },
+{ "TPM_DEFEND_LOCK_RUNNING", TPM_E_BASE + TPM_E_NON_FATAL + 3,
+"The TPM is defending against dictionary attacks and is in some\n\
+time-out period" },
+ };
+
+#endif /* TPM_ERROR_MESSAGES_H */
diff --git a/src/vendorcode/google/chromeos/antirollback.c b/src/vendorcode/google/chromeos/antirollback.c new file mode 100644 index 0000000000..306e90329b --- /dev/null +++ b/src/vendorcode/google/chromeos/antirollback.c @@ -0,0 +1,676 @@ +/* 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 "sysincludes.h" + +#include "crc8.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) { \ + VBDEBUG(("Rollback: %08x returned by " #tpm_command \ + "\n", (int)result_)); \ + return result_; \ + } \ + } while (0) + + +uint32_t TPMClearAndReenable(void) +{ + VBDEBUG(("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; + } +} + +uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size) +{ + uint32_t result = TlclDefineSpace(index, perm, size); + if (result == TPM_E_MAXNVWRITES) { + RETURN_ON_FAILURE(TPMClearAndReenable()); + return TlclDefineSpace(index, perm, size); + } 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 == Crc8(rsf, + offsetof(RollbackSpaceFirmware, crc8))) + return TPM_SUCCESS; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + 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 = 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; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + /* Try writing it again. Maybe it was garbled on the way out. */ + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; +} + +uint32_t SetVirtualDevMode(int val) +{ + RollbackSpaceFirmware rsf; + + VBDEBUG(("TPM: Entering %s()\n", __func__)); + if (TPM_SUCCESS != ReadSpaceFirmware(&rsf)) + return VBERROR_TPM_FIRMWARE_SETUP; + + VBDEBUG(("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 by SetupTPM() on the next boot. + */ + VBDEBUG(("TPM: flags are now 0x%02x\n", rsf.flags)); + + if (TPM_SUCCESS != WriteSpaceFirmware(&rsf)) + return VBERROR_TPM_SET_BOOT_MODE_STATE; + + VBDEBUG(("TPM: Leaving %s()\n", __func__)); + 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 == Crc8(rsk, offsetof(RollbackSpaceKernel, crc8))) + return TPM_SUCCESS; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + 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 = 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; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + /* Try writing it again. Maybe it was garbled on the way out. */ + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; +} + +uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware *rsf, + RollbackSpaceKernel *rsk) +{ + static const RollbackSpaceFirmware rsf_init = { + .struct_version = ROLLBACK_SPACE_FIRMWARE_VERSION, + }; + static const RollbackSpaceKernel rsk_init = { + .struct_version = ROLLBACK_SPACE_KERNEL_VERSION, + .uid = ROLLBACK_SPACE_KERNEL_UID, + }; + TPM_PERMANENT_FLAGS pflags; + uint32_t result; + + VBDEBUG(("TPM: One-time initialization\n")); + + /* + * Do a full test. This only happens the first time the device is + * turned on in the factory, so performance is not an issue. This is + * almost certainly not necessary, but it gives us more confidence + * about some code paths below that are difficult to + * test---specifically the ones that set lifetime flags, and are only + * executed once per physical TPM. + */ + result = TlclSelfTestFull(); + if (result != TPM_SUCCESS) + return result; + + result = TlclGetPermanentFlags(&pflags); + if (result != TPM_SUCCESS) + return result; + + /* + * TPM may come from the factory without physical presence finalized. + * Fix if necessary. + */ + VBDEBUG(("TPM: physicalPresenceLifetimeLock=%d\n", + pflags.physicalPresenceLifetimeLock)); + if (!pflags.physicalPresenceLifetimeLock) { + VBDEBUG(("TPM: Finalizing physical presence\n")); + RETURN_ON_FAILURE(TlclFinalizePhysicalPresence()); + } + + /* + * The TPM will not enforce the NV authorization restrictions until the + * execution of a TPM_NV_DefineSpace with the handle of + * TPM_NV_INDEX_LOCK. Here we create that space if it doesn't already + * exist. */ + VBDEBUG(("TPM: nvLocked=%d\n", pflags.nvLocked)); + if (!pflags.nvLocked) { + VBDEBUG(("TPM: Enabling NV locking\n")); + RETURN_ON_FAILURE(TlclSetNvLocked()); + } + + /* Clear TPM owner, in case the TPM is already owned for some reason. */ + VBDEBUG(("TPM: Clearing owner\n")); + RETURN_ON_FAILURE(TPMClearAndReenable()); + + /* Initializes the firmware and kernel spaces */ + Memcpy(rsf, &rsf_init, sizeof(RollbackSpaceFirmware)); + Memcpy(rsk, &rsk_init, sizeof(RollbackSpaceKernel)); + + /* Define the backup space. No need to initialize it, though. */ + RETURN_ON_FAILURE(SafeDefineSpace( + BACKUP_NV_INDEX, TPM_NV_PER_PPWRITE, BACKUP_NV_SIZE)); + + /* Define and initialize the kernel space */ + RETURN_ON_FAILURE(SafeDefineSpace(KERNEL_NV_INDEX, TPM_NV_PER_PPWRITE, + sizeof(RollbackSpaceKernel))); + RETURN_ON_FAILURE(WriteSpaceKernel(rsk)); + + /* Do the firmware space last, so we retry if we don't get this far. */ + RETURN_ON_FAILURE(SafeDefineSpace( + FIRMWARE_NV_INDEX, + TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE, + sizeof(RollbackSpaceFirmware))); + RETURN_ON_FAILURE(WriteSpaceFirmware(rsf)); + + return TPM_SUCCESS; +} + + +/* + * SetupTPM starts the TPM and establishes the root of trust for the + * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a + * TPM hardware failure. 3 An unexpected TPM state due to some attack. In + * general we cannot easily distinguish the kind of failure, so our strategy is + * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM + * again, which executes (almost) the same sequence of operations. There is a + * good chance that, if recovery mode was entered because of a TPM failure, the + * failure will repeat itself. (In general this is impossible to guarantee + * because we have no way of creating the exact TPM initial state at the + * previous boot.) In recovery mode, we ignore the failure and continue, thus + * giving the recovery kernel a chance to fix things (that's why we don't set + * bGlobalLock). The choice is between a knowingly insecure device and a + * bricked device. + * + * As a side note, observe that we go through considerable hoops to avoid using + * the STCLEAR permissions for the index spaces. We do this to avoid writing + * to the TPM flashram at every reboot or wake-up, because of concerns about + * the durability of the NVRAM. + */ +uint32_t SetupTPM(int developer_mode, int disable_dev_request, + int clear_tpm_owner_request, RollbackSpaceFirmware* rsf) +{ + uint8_t in_flags; + uint8_t disable; + uint8_t deactivated; + uint32_t result; + uint32_t versions; + + RETURN_ON_FAILURE(TlclLibInit()); + +#ifdef TEGRA_SOFT_REBOOT_WORKAROUND + result = TlclStartup(); + if (result == TPM_E_INVALID_POSTINIT) { + /* + * Some prototype hardware doesn't reset the TPM on a CPU + * reset. We do a hard reset to get around this. + */ + VBDEBUG(("TPM: soft reset detected\n", result)); + return TPM_E_MUST_REBOOT; + } else if (result != TPM_SUCCESS) { + VBDEBUG(("TPM: TlclStartup returned %08x\n", result)); + return result; + } +#else + RETURN_ON_FAILURE(TlclStartup()); +#endif + + /* + * Some TPMs start the self test automatically at power on. In that case we + * don't need to call ContinueSelfTest. On some (other) TPMs, + * ContinueSelfTest may block. In that case, we definitely don't want to + * call it here. For TPMs in the intersection of these two sets, we're + * screwed. (In other words: TPMs that require manually starting the + * self-test AND block will have poor performance until we split + * TlclSendReceive() into Send() and Receive(), and have a state machine to + * control setup.) + * + * This comment is likely to become obsolete in the near future, so don't + * trust it. It may have not been updated. + */ +#ifdef TPM_MANUAL_SELFTEST +#ifdef TPM_BLOCKING_CONTINUESELFTEST +#warning "lousy TPM!" +#endif + RETURN_ON_FAILURE(TlclContinueSelfTest()); +#endif + result = TlclAssertPhysicalPresence(); + if (result != TPM_SUCCESS) { + /* + * It is possible that the TPM was delivered with the physical + * presence command disabled. This tries enabling it, then + * tries asserting PP again. + */ + RETURN_ON_FAILURE(TlclPhysicalPresenceCMDEnable()); + RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); + } + + /* Check that the TPM is enabled and activated. */ + RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated, NULL)); + if (disable || deactivated) { + VBDEBUG(("TPM: disabled (%d) or deactivated (%d). Fixing...\n", + disable, deactivated)); + RETURN_ON_FAILURE(TlclSetEnable()); + RETURN_ON_FAILURE(TlclSetDeactivated(0)); + VBDEBUG(("TPM: Must reboot to re-enable\n")); + return TPM_E_MUST_REBOOT; + } + + /* Read the firmware space. */ + result = ReadSpaceFirmware(rsf); + if (TPM_E_BADINDEX == result) { + RollbackSpaceKernel rsk; + + /* + * This is the first time we've run, and the TPM has not been + * initialized. Initialize it. + */ + VBDEBUG(("TPM: Not initialized yet.\n")); + RETURN_ON_FAILURE(OneTimeInitializeTPM(rsf, &rsk)); + } else if (TPM_SUCCESS != result) { + VBDEBUG(("TPM: Firmware space in a bad state; giving up.\n")); + return TPM_E_CORRUPTED_STATE; + } + Memcpy(&versions, &rsf->fw_versions, sizeof(versions)); + VBDEBUG(("TPM: Firmware space sv%d f%x v%x\n", + rsf->struct_version, rsf->flags, versions)); + in_flags = rsf->flags; + + /* If we've been asked to clear the virtual dev-mode flag, do so now */ + if (disable_dev_request) { + rsf->flags &= ~FLAG_VIRTUAL_DEV_MODE_ON; + VBDEBUG(("TPM: Clearing virt dev-switch: f%x\n", rsf->flags)); + } + + /* + * The developer_mode value that's passed in is only set by a hardware + * dev-switch. We should OR it with the virtual switch, whether or not + * the virtual switch is used. If it's not used, it shouldn't change, + * so it doesn't matter. + */ + if (rsf->flags & FLAG_VIRTUAL_DEV_MODE_ON) + developer_mode = 1; + + /* + * Clear ownership if developer flag has toggled, or if an owner-clear + * has been requested. + */ + if ((developer_mode ? FLAG_LAST_BOOT_DEVELOPER : 0) != + (in_flags & FLAG_LAST_BOOT_DEVELOPER)) { + VBDEBUG(("TPM: Developer flag changed; clearing owner.\n")); + RETURN_ON_FAILURE(TPMClearAndReenable()); + } else if (clear_tpm_owner_request) { + VBDEBUG(("TPM: Clearing owner as specifically requested.\n")); + RETURN_ON_FAILURE(TPMClearAndReenable()); + } + + if (developer_mode) + rsf->flags |= FLAG_LAST_BOOT_DEVELOPER; + else + rsf->flags &= ~FLAG_LAST_BOOT_DEVELOPER; + + + /* If firmware space is dirty, flush it back to the TPM */ + if (rsf->flags != in_flags) { + VBDEBUG(("TPM: Updating firmware space.\n")); + RETURN_ON_FAILURE(WriteSpaceFirmware(rsf)); + } + + VBDEBUG(("TPM: SetupTPM() succeeded\n")); + return TPM_SUCCESS; +} + + +#ifdef DISABLE_ROLLBACK_TPM +/* Dummy implementations which don't support TPM rollback protection */ + +uint32_t RollbackS3Resume(void) +{ +#ifndef CHROMEOS_ENVIRONMENT + /* + * Initialize the TPM, but ignore return codes. In ChromeOS + * environment, don't even talk to the TPM. + */ + TlclLibInit(); + TlclResume(); +#endif + return TPM_SUCCESS; +} + +uint32_t RollbackFirmwareSetup(int is_hw_dev, + int disable_dev_request, + int clear_tpm_owner_request, + int *is_virt_dev, uint32_t *version) +{ +#ifndef CHROMEOS_ENVIRONMENT + /* + * Initialize the TPM, but ignores return codes. In ChromeOS + * environment, don't even talk to the TPM. + */ + TlclLibInit(); + TlclStartup(); + TlclContinueSelfTest(); +#endif + *is_virt_dev = 0; + *version = 0; + return TPM_SUCCESS; +} + +uint32_t RollbackFirmwareWrite(uint32_t version) +{ + return TPM_SUCCESS; +} + +uint32_t RollbackFirmwareLock(void) +{ + return TPM_SUCCESS; +} + +uint32_t RollbackKernelRead(uint32_t* version) +{ + *version = 0; + return TPM_SUCCESS; +} + +uint32_t RollbackKernelWrite(uint32_t version) +{ + return TPM_SUCCESS; +} + +uint32_t RollbackBackupRead(uint8_t *raw) +{ + return TPM_SUCCESS; +} + +uint32_t RollbackBackupWrite(uint8_t *raw) +{ + return TPM_SUCCESS; +} + +uint32_t RollbackKernelLock(int recovery_mode) +{ + return TPM_SUCCESS; +} + +#else + +uint32_t RollbackS3Resume(void) +{ + uint32_t result; + RETURN_ON_FAILURE(TlclLibInit()); + result = TlclResume(); + if (result == TPM_E_INVALID_POSTINIT) { + /* + * We're on a platform where the TPM maintains power in S3, so + * it's already initialized. + */ + return TPM_SUCCESS; + } + return result; +} + +uint32_t RollbackFirmwareSetup(int is_hw_dev, + int disable_dev_request, + int clear_tpm_owner_request, + int *is_virt_dev, uint32_t *version) +{ + RollbackSpaceFirmware rsf; + + /* Set version to 0 in case we fail */ + *version = 0; + + RETURN_ON_FAILURE(SetupTPM(is_hw_dev, disable_dev_request, + clear_tpm_owner_request, &rsf)); + Memcpy(version, &rsf.fw_versions, sizeof(*version)); + *is_virt_dev = (rsf.flags & FLAG_VIRTUAL_DEV_MODE_ON) ? 1 : 0; + VBDEBUG(("TPM: RollbackFirmwareSetup %x\n", (int)*version)); + return TPM_SUCCESS; +} + +uint32_t RollbackFirmwareWrite(uint32_t version) +{ + RollbackSpaceFirmware rsf; + uint32_t old_version; + + RETURN_ON_FAILURE(ReadSpaceFirmware(&rsf)); + Memcpy(&old_version, &rsf.fw_versions, sizeof(old_version)); + VBDEBUG(("TPM: RollbackFirmwareWrite %x --> %x\n", (int)old_version, + (int)version)); + Memcpy(&rsf.fw_versions, &version, sizeof(version)); + return WriteSpaceFirmware(&rsf); +} + +uint32_t RollbackFirmwareLock(void) +{ + return TlclSetGlobalLock(); +} + +uint32_t RollbackKernelRead(uint32_t* version) +{ + RollbackSpaceKernel rsk; + uint32_t perms, uid; + + /* + * 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)); + 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; + + Memcpy(version, &rsk.kernel_versions, sizeof(*version)); + VBDEBUG(("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)); + VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", + (int)old_version, (int)version)); + Memcpy(&rsk.kernel_versions, &version, sizeof(version)); + return WriteSpaceKernel(&rsk); +} + +/* + * We don't really care whether the TPM owner has been messing with this or + * not. We lock it along with the Kernel space just to avoid problems, but it's + * only useful in dev-mode and only when the battery has been drained + * completely. There aren't any security issues. It's just in the TPM because + * we don't have any other place to keep it. + */ +uint32_t RollbackBackupRead(uint8_t *raw) +{ + uint32_t r; + r = TlclRead(BACKUP_NV_INDEX, raw, BACKUP_NV_SIZE); + VBDEBUG(("TPM: %s returning 0x%x\n", __func__, r)); + return r; +} + +uint32_t RollbackBackupWrite(uint8_t *raw) +{ + uint32_t r; + r = TlclWrite(BACKUP_NV_INDEX, raw, BACKUP_NV_SIZE); + VBDEBUG(("TPM: %s returning 0x%x\n", __func__, r)); + return r; +} + +uint32_t RollbackKernelLock(int recovery_mode) +{ + if (recovery_mode) + return TPM_SUCCESS; + else + return TlclLockPhysicalPresence(); +} + +#endif /* DISABLE_ROLLBACK_TPM */ |