diff options
Diffstat (limited to 'common/ccd_config.c')
-rw-r--r-- | common/ccd_config.c | 1553 |
1 files changed, 0 insertions, 1553 deletions
diff --git a/common/ccd_config.c b/common/ccd_config.c deleted file mode 100644 index 91232df295..0000000000 --- a/common/ccd_config.c +++ /dev/null @@ -1,1553 +0,0 @@ -/* Copyright 2017 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. - * - * Case Closed Debug configuration - */ - -#include "common.h" -#include "byteorder.h" -#include "ccd_config.h" -#include "console.h" -#include "cryptoc/sha256.h" -#include "cryptoc/util.h" -#include "dcrypto.h" -#include "extension.h" -#include "hooks.h" -#include "nvmem_vars.h" -#include "physical_presence.h" -#include "system.h" -#include "system_chip.h" -#include "task.h" -#include "timer.h" -#include "tpm_registers.h" -#include "tpm_vendor_cmds.h" -#include "trng.h" -#include "wp.h" - -#define CPRINTS(format, args...) cprints(CC_CCD, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_CCD, format, ## args) - -/* Let's make sure that CCD capability state enum fits into two bits. */ -BUILD_ASSERT(CCD_CAP_STATE_COUNT <= 4); - -/* Restriction state for ccdunlock when no password is set */ -enum ccd_unlock_restrict { - /* Unrestricted */ - CCD_UNLOCK_UNRESTRICTED = 0, - - /* Physical presence required for unlock unless disabled by config */ - CCD_UNLOCK_NEED_PP, - - /* Unlock not allowed */ - CCD_UNLOCK_DISABLED -}; - -/* Minimum time between password attempts */ -#define PASSWORD_RATE_LIMIT_US (3 * SECOND) - -/* Current version of case-closed debugging configuration struct */ -#define CCD_CONFIG_VERSION 0x10 - -/* - * CCD command header; including the subcommand code used to demultiplex - * various CCD commands over the same TPM vendor command. - */ -struct ccd_vendor_cmd_header { - struct tpm_cmd_header tpm_header; - - /* On input, the subcommand. On output, may contain EC return code */ - uint8_t ccd_subcommand; -} __packed; - -/* Size of password salt and digest in bytes */ -#define CCD_PASSWORD_SALT_SIZE 4 -#define CCD_PASSWORD_DIGEST_SIZE 16 - -/* Way longer than practical. */ -#define CCD_MAX_PASSWORD_SIZE 40 - -struct ccd_config { - /* Version (CCD_CONFIG_VERSION) */ - uint8_t version; - - /* - * Flags. These MUST immediately follow version, so that the test - * lab flag is always the LSBit of the first flags byte. - */ - uint8_t flags[3]; - - /* Capabilities */ - uint8_t capabilities[8]; - - /* Password salt (random) */ - uint8_t password_salt[CCD_PASSWORD_SALT_SIZE]; - - /* - * Password digest = truncated - * SHA256_digest(password_salt+device_id+password) - */ - uint8_t password_digest[CCD_PASSWORD_DIGEST_SIZE]; -}; - -/* Nvmem variable name for CCD config */ -static const uint8_t k_ccd_config = NVMEM_VAR_CCD_CONFIG; - -/* Flags which can be set via ccd_set_flag() */ -static const uint32_t k_public_flags = - CCD_FLAG_OVERRIDE_WP_AT_BOOT | - CCD_FLAG_OVERRIDE_WP_STATE_ENABLED | - CCD_FLAG_OVERRIDE_BATT_AT_BOOT | - CCD_FLAG_OVERRIDE_BATT_STATE_CONNECT; - -/* List of CCD capability info; must be in same order as enum ccd_capability */ -static const struct ccd_capability_info cap_info[CCD_CAP_COUNT] = CAP_INFO_DATA; - -static const char *ccd_state_names[CCD_STATE_COUNT] = CCD_STATE_NAMES; -static const char *ccd_cap_state_names[CCD_CAP_STATE_COUNT] = - CCD_CAP_STATE_NAMES; - -static enum ccd_state ccd_state = CCD_STATE_LOCKED; -static struct ccd_config config; -static uint8_t ccd_config_loaded; -static uint8_t force_disabled; -static struct mutex ccd_config_mutex; -static uint8_t ccd_console_active; /* CCD console command is in progress. */ - -/******************************************************************************/ -/* Raw config accessors */ - -/** - * Get CCD flags. - * - * @return the current flags mask. - */ -static uint32_t raw_get_flags(void) -{ - return (uint32_t)(config.flags[0] << 0) - | ((uint32_t)config.flags[1] << 8) - | ((uint32_t)config.flags[2] << 16); -} - -/** - * Set a single CCD flag. - * - * This does NOT call ccd_save_config() or lock the mutex. Caller must do - * those. - * - * @param flag Flag to set - * @param value New value for flag (0=clear, non-zero=set) - */ -static void raw_set_flag(enum ccd_flag flag, int value) -{ - uint32_t f; - - f = raw_get_flags(); - if (value) - f |= flag; - else - f &= ~flag; - - config.flags[0] = (uint8_t)(f >> 0); - config.flags[1] = (uint8_t)(f >> 8); - config.flags[2] = (uint8_t)(f >> 16); -} - -/** - * Get a raw capability state from the config - * - * @param cap Capability to check - * @param translate_default If non-zero, translate CCD_CAP_STATE_DEFAULT - * to the actual default for that config - * @return The capability state. - */ -static enum ccd_capability_state raw_get_cap(enum ccd_capability cap, - int translate_default) -{ - const uint32_t index = cap / CCD_CAPS_PER_BYTE; - const uint32_t shift = (cap % CCD_CAPS_PER_BYTE) * CCD_CAP_BITS; - - int c = (config.capabilities[index] >> shift) & CCD_CAP_BITMASK; - - if (c == CCD_CAP_STATE_DEFAULT && translate_default) - c = cap_info[cap].default_state; - - return c; -} - -/** - * Set a raw capability to the config. - * - * This does NOT call ccd_save_config() or lock the mutex. Caller must do - * those. - * - * @param cap Capability to set - * @param state New state for capability - */ -static void raw_set_cap(enum ccd_capability cap, - enum ccd_capability_state state) -{ - const uint32_t index = cap / CCD_CAPS_PER_BYTE; - const uint32_t shift = (cap % CCD_CAPS_PER_BYTE) * CCD_CAP_BITS; - - config.capabilities[index] &= ~(CCD_CAP_BITMASK << shift); - config.capabilities[index] |= (state & CCD_CAP_BITMASK) << shift; -} - -/** - * Check CCD configuration is reset to default value. - * - * @return 1 if it is in default mode. - * 0 otherwise. - */ -static int raw_check_all_caps_default(void) -{ - uint32_t i; - - for (i = 0; i < CCD_CAP_COUNT; i++) - if (raw_get_cap(i, 0) != CCD_CAP_STATE_DEFAULT) - return 0; - - return 1; -} - -/** - * Check if a password is set. - * @return 1 if password is set, 0 if it's not - */ -static int raw_has_password(void) -{ - uint8_t set = 0; - int i; - - /* Password is set unless salt and digest are all zero */ - for (i = 0; i < sizeof(config.password_salt); i++) - set |= config.password_salt[i]; - for (i = 0; i < sizeof(config.password_digest); i++) - set |= config.password_digest[i]; - - return !!set; -} - -/** - * Calculate the expected digest for a password. - * - * Uses the unique device ID and the salt from the config. - * - * @param digest Pointer to a CCD_PASSWORD_DIGEST_SIZE buffer - * @param password The password to digest - */ -static void ccd_password_digest(uint8_t *digest, const char *password) -{ - HASH_CTX sha; - uint8_t *unique_id; - int unique_id_len; - - unique_id_len = system_get_chip_unique_id(&unique_id); - - DCRYPTO_SHA256_init(&sha, 0); - HASH_update(&sha, config.password_salt, sizeof(config.password_salt)); - HASH_update(&sha, unique_id, unique_id_len); - HASH_update(&sha, password, strlen(password)); - memcpy(digest, HASH_final(&sha), CCD_PASSWORD_DIGEST_SIZE); -} - -/** - * Check the password. - * - * @param password The password to check - * @return EC_SUCCESS, EC_ERROR_BUSY if too soon since last attempt, or - * EC_ERROR_ACCESS_DENIED if mismatch. - */ -static int raw_check_password(const char *password) -{ - /* - * Time of last password attempt; initialized to 0 at boot. Yes, we're - * only keeping the bottom 32 bits of the timer here, so on a - * wraparound (every ~4000 seconds) it's possible for an attacker to - * get one extra attempt. But it still behaves properly at boot, - * requiring the system to be up PASSWORD_RATE_LIMIT_US before allowing - * the first attempt. - */ - static uint32_t last_password_time; - - uint8_t digest[CCD_PASSWORD_DIGEST_SIZE]; - uint32_t t; - - /* If no password is set, match only an empty password */ - if (!raw_has_password()) - return *password ? EC_ERROR_ACCESS_DENIED : EC_SUCCESS; - - /* Rate limit password attempts */ - t = get_time().le.lo; - if (t - last_password_time < PASSWORD_RATE_LIMIT_US) - return EC_ERROR_BUSY; - last_password_time = t; - - /* Calculate the digest of the password */ - ccd_password_digest(digest, password); - - if (safe_memcmp(digest, config.password_digest, - sizeof(config.password_digest))) - return EC_ERROR_ACCESS_DENIED; - - return EC_SUCCESS; -} - -/** - * Clear the password. - * - * This does NOT call ccd_save_config() or lock the mutex. Caller must do - * those. - */ -static void raw_reset_password(void) -{ - memset(config.password_salt, 0, sizeof(config.password_salt)); - memset(config.password_digest, 0, sizeof(config.password_digest)); - raw_set_flag(CCD_FLAG_PASSWORD_SET_WHEN_UNLOCKED, 0); -} - -/** - * Set the password. - * - * @param password New password; must be non-empty - */ -static void raw_set_password(const char *password) -{ - /* Get a new salt */ - rand_bytes(config.password_salt, sizeof(config.password_salt)); - - /* Update the password digest */ - ccd_password_digest(config.password_digest, password); - - /* Track whether we were opened when we set the password */ - raw_set_flag(CCD_FLAG_PASSWORD_SET_WHEN_UNLOCKED, - ccd_state == CCD_STATE_UNLOCKED); -} - -/******************************************************************************/ -/* Internal methods */ - -/** - * Set the CCD state. - * - * @param state New CCD state - */ -static void ccd_set_state(enum ccd_state state) -{ - if (state == ccd_state) - return; - - ccd_state = state; - - /* Notify CCD users of configuration change */ - hook_notify(HOOK_CCD_CHANGE); -} - -/** - * Load CCD config from nvmem_vars - * - * @return EC_SUCCESS or non-zero error code. - */ -static void ccd_load_config(void) -{ - const struct tuple *t; - - /* Don't reload if we're already loaded */ - if (ccd_config_loaded) - return; - - /* Load config data from nvmem */ - t = getvar(&k_ccd_config, sizeof(k_ccd_config)); - - /* Use defaults if config data is not present */ - if (!t) { - if (board_is_first_factory_boot()) { - /* Give factory/RMA access */ - CPRINTS("CCD using factory config"); - ccd_reset_config(CCD_RESET_FACTORY); - } else { - /* Somehow we lost our config; normal defaults */ - CPRINTS("CCD using default config"); - ccd_reset_config(CCD_RESET_TEST_LAB); - } - goto ccd_is_loaded; - } - - /* Copy the tuple data */ - memcpy(&config, tuple_val(t), MIN(sizeof(config), t->val_len)); - - /* If version or size is wrong, reset to defaults */ - if (config.version != CCD_CONFIG_VERSION || - t->val_len != sizeof(config)) { - CPRINTS("CCD config mismatch; using defaults"); - /* - * If the config data was big enough to hold the test lab bit, - * preserve it. That's guaranteed to be in the same place for - * all data versions. - */ - ccd_reset_config(t->val_len < 2 ? CCD_RESET_TEST_LAB : 0); - } - - freevar(t); - -ccd_is_loaded: - ccd_config_loaded = 1; - - /* Notify CCD users of configuration change */ - hook_notify(HOOK_CCD_CHANGE); -} - -/** - * Save CCD config to nvmem_vars - * - * @return EC_SUCCESS or non-zero error code. - */ -static int ccd_save_config(void) -{ - int rv; - - rv = setvar(&k_ccd_config, sizeof(k_ccd_config), - (const uint8_t *)&config, sizeof(config)); - if (rv) - return rv; - - /* - * Notify CCD users of configuration change. - * Protect this notify with the ccd_config_loaded flag so recipients of - * HOOK_CCD_CHANGE don't call ccd_get/ccd_set before the CCD - * initialization is complete. - */ - if (ccd_config_loaded) - hook_notify(HOOK_CCD_CHANGE); - - return rv; -} - -/** - * Set a CCD capability to a new state. - * - * @param cap Capability to set - * @param state New state for capability - * @return EC_SUCCESS or non-zero error code. - */ -static int ccd_set_cap(enum ccd_capability cap, enum ccd_capability_state state) -{ - if (!ccd_config_loaded) - return EC_ERROR_BUSY; - - if (state == raw_get_cap(cap, 0)) - return EC_SUCCESS; /* Capability not changed */ - - mutex_lock(&ccd_config_mutex); - raw_set_cap(cap, state); - mutex_unlock(&ccd_config_mutex); - - return ccd_save_config(); -} - -int ccd_reset_config(unsigned int flags) -{ - int old_lab = ccd_get_flag(CCD_FLAG_TEST_LAB); - - mutex_lock(&ccd_config_mutex); - - if (flags & CCD_RESET_UNLOCKED_ONLY) { - /* Only set config options that are mutable when unlocked */ - int i; - - /* Reset the password if it was set when unlocked */ - if (ccd_get_flag(CCD_FLAG_PASSWORD_SET_WHEN_UNLOCKED)) - raw_reset_password(); - - /* Reset all capabilities that aren't IfOpened */ - for (i = 0; i < CCD_CAP_COUNT; i++) { - if (raw_get_cap(i, 1) == CCD_CAP_STATE_IF_OPENED) - continue; - raw_set_cap(i, CCD_CAP_STATE_DEFAULT); - } - - /* Flags all require IfOpened, so don't touch those */ - } else { - /* Reset the entire config */ - memset(&config, 0, sizeof(config)); - config.version = CCD_CONFIG_VERSION; - /* Update write protect after resetting the config */ - board_wp_follow_ccd_config(); - } - - if (flags & CCD_RESET_FACTORY) { - /* Force factory mode settings */ - int i; - - /* Allow all capabilities all the time */ - for (i = 0; i < CCD_CAP_COUNT; i++) - raw_set_cap(i, CCD_CAP_STATE_ALWAYS); - - raw_set_flag(CCD_FLAG_FACTORY_MODE_ENABLED, 1); - - /* Force WP disabled at boot */ - raw_set_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT, 1); - raw_set_flag(CCD_FLAG_OVERRIDE_WP_STATE_ENABLED, 0); - board_wp_follow_ccd_config(); - } - - /* Restore test lab flag unless explicitly resetting it */ - if (!(flags & CCD_RESET_TEST_LAB)) - raw_set_flag(CCD_FLAG_TEST_LAB, old_lab); - - mutex_unlock(&ccd_config_mutex); - - return ccd_save_config(); -} - -/** - * Convert a string to a capability index. - * - * @param name Capability name to find - * @return The capability index, or CCD_CAP_COUNT if error - */ -static enum ccd_capability ccd_cap_from_name(const char *name) -{ - int i; - - for (i = 0; i < CCD_CAP_COUNT; i++) { - if (!strcasecmp(name, cap_info[i].name)) - return i; - } - - return CCD_CAP_COUNT; -} - -/** - * Reset the password. - * - * @return EC_SUCCESS or non-zero error code. - */ -static int ccd_reset_password(void) -{ - mutex_lock(&ccd_config_mutex); - raw_reset_password(); - mutex_unlock(&ccd_config_mutex); - - return ccd_save_config(); -} - -/** - * Set the password. - * - * @param password New password; must be non-empty - * @return EC_SUCCESS or non-zero error code. - */ -static int ccd_set_password(const char *password) -{ - mutex_lock(&ccd_config_mutex); - raw_set_password(password); - mutex_unlock(&ccd_config_mutex); - - return ccd_save_config(); -} - -/******************************************************************************/ -/* Handlers for state changes requiring physical presence */ - -/* - * Could be invoked synchronously on the TPM task context, or asynchronously, - * after physical presence is established, on the hooks task context. - * - * The appropriate TPM reset entry point needs to be invoked. Also, make sure - * that the board is always rebooted when TPM is reset. - * - * @param sync Non-zero to invoke synchronously. - */ -static void ccd_open_done(int sync) -{ - int rv; - - /* - * Wiping the TPM may take a while. Delay sleep long enough for the - * open process to finish. - */ - delay_sleep_by(DISABLE_SLEEP_TIME_TPM_WIPE); - - if (!ccd_is_cap_enabled(CCD_CAP_OPEN_WITHOUT_TPM_WIPE)) { - /* Can't open unless wipe succeeds */ - if (sync) - rv = tpm_sync_reset(1); - else - rv = board_wipe_tpm(1); - - if (rv != EC_SUCCESS) { - CPRINTS("CCD open TPM wipe failed"); - return; - } - } - - if (!ccd_is_cap_enabled(CCD_CAP_UNLOCK_WITHOUT_AP_REBOOT) || - (!ccd_is_cap_enabled(CCD_CAP_OPEN_WITHOUT_TPM_WIPE) && sync)) - board_reboot_ap(); - - CPRINTS("CCD opened"); - ccd_set_state(CCD_STATE_OPENED); -} - -static void ccd_open_done_async(void) -{ - ccd_open_done(0); -} - -static void ccd_unlock_done(void) -{ - if (!ccd_is_cap_enabled(CCD_CAP_UNLOCK_WITHOUT_AP_REBOOT)) - board_reboot_ap(); - - CPRINTS("CCD unlocked"); - ccd_set_state(CCD_STATE_UNLOCKED); -} - -static void ccd_testlab_toggle(void) -{ - int v = !ccd_get_flag(CCD_FLAG_TEST_LAB); - - /* Use raw_set_flag() because the test lab flag is internal */ - mutex_lock(&ccd_config_mutex); - raw_set_flag(CCD_FLAG_TEST_LAB, v); - mutex_unlock(&ccd_config_mutex); - - if (ccd_save_config() == EC_SUCCESS) - CPRINTS("CCD test lab mode %sbled", v ? "ena" : "disa"); - else - CPRINTS("Error setting CCD test lab mode!"); -} - -/******************************************************************************/ -/* External interface */ - -int ccd_has_password(void) -{ - return raw_has_password(); -} - -void ccd_config_init(enum ccd_state state) -{ - /* Set initial state, after making sure it's a valid one */ - if (state != CCD_STATE_UNLOCKED && state != CCD_STATE_OPENED) - state = CCD_STATE_LOCKED; - ccd_state = state; - - ccd_load_config(); -} - -int ccd_get_flag(enum ccd_flag flag) -{ - uint32_t f = raw_get_flags(); - - if (!ccd_config_loaded || force_disabled) - return 0; - - return !!(f & flag); -} - -int ccd_set_flag(enum ccd_flag flag, int value) -{ - if (force_disabled) - return EC_ERROR_ACCESS_DENIED; - - /* Fail if trying to set a private flag */ - if (flag & ~k_public_flags) - return EC_ERROR_ACCESS_DENIED; - - if (!ccd_config_loaded) - return EC_ERROR_BUSY; - - if (ccd_get_flag(flag) == !!value) - return EC_SUCCESS; - - mutex_lock(&ccd_config_mutex); - raw_set_flag(flag, value); - mutex_unlock(&ccd_config_mutex); - return ccd_save_config(); -} - -int ccd_is_cap_enabled(enum ccd_capability cap) -{ - if (!ccd_config_loaded || force_disabled) - return 0; - - switch (raw_get_cap(cap, 1)) { - case CCD_CAP_STATE_ALWAYS: - return 1; - case CCD_CAP_STATE_UNLESS_LOCKED: - return ccd_state != CCD_STATE_LOCKED; - case CCD_CAP_STATE_IF_OPENED: - default: - return ccd_state == CCD_STATE_OPENED; - } -} - -enum ccd_state ccd_get_state(void) -{ - return ccd_state; -} - -void ccd_disable(void) -{ - CPRINTS("CCD disabled"); - force_disabled = 1; - ccd_set_state(CCD_STATE_LOCKED); -} - -int ccd_get_factory_mode(void) -{ - return ccd_get_flag(CCD_FLAG_FACTORY_MODE_ENABLED); -} - -/******************************************************************************/ -/* Console commands */ - -static int command_ccd_info(void) -{ - int i; - - ccprintf("State: %s%s\n", ccd_state_names[ccd_state], - force_disabled ? " (Disabled)" : ""); - ccprintf("Password: %s\n", raw_has_password() ? "set" : "none"); - ccprintf("Flags: 0x%06x\n", raw_get_flags()); - - ccprintf("Capabilities: %ph\n", HEX_BUF(config.capabilities, 8)); - for (i = 0; i < CCD_CAP_COUNT; i++) { - int c = raw_get_cap(i, 0); - - ccprintf(" %-15s %c %d=%s", - cap_info[i].name, - ccd_is_cap_enabled(i) ? 'Y' : '-', - c, ccd_cap_state_names[c]); - if (c == CCD_CAP_STATE_DEFAULT) - ccprintf(" (%s)", - ccd_cap_state_names[cap_info[i].default_state]); - ccprintf("\n"); - cflush(); - } - - ccprintf("TPM:%s%s\n", - board_fwmp_allows_unlock() ? "" : " fwmp_lock", - board_vboot_dev_mode_enabled() ? " dev_mode" : ""); - - ccprintf("Capabilities are %s.\n", raw_check_all_caps_default() ? - "default" : "modified"); - - ccputs("Use 'ccd help' to print subcommands\n"); - return EC_SUCCESS; -} - -static int command_ccd_reset(int argc, char **argv) -{ - int flags = 0; - - if (argc > 1) { - if (!strcasecmp(argv[1], "factory")) - flags = CCD_RESET_FACTORY; - else - return EC_ERROR_PARAM1; - } - - switch (ccd_state) { - case CCD_STATE_OPENED: - ccprintf("%s settings.\n", flags & CCD_RESET_FACTORY ? - "Opening factory " : "Resetting all"); - /* Note that this does not reset the testlab flag */ - return ccd_reset_config(flags); - - case CCD_STATE_UNLOCKED: - ccprintf("Resetting unlocked settings.\n"); - return ccd_reset_config(CCD_RESET_UNLOCKED_ONLY); - - default: - return EC_ERROR_ACCESS_DENIED; - } -} - -static int command_ccd_set(int argc, char **argv) -{ - enum ccd_capability cap; - enum ccd_capability_state old; - enum ccd_capability_state new; - - /* Only works if unlocked or opened */ - if (ccd_state == CCD_STATE_LOCKED) - return EC_ERROR_ACCESS_DENIED; - - if (argc < 3) - return EC_ERROR_PARAM_COUNT; - - /* Get capability to set */ - cap = ccd_cap_from_name(argv[1]); - if (cap == CCD_CAP_COUNT) - return EC_ERROR_PARAM1; - - /* Get new state */ - for (new = CCD_CAP_STATE_DEFAULT; new < CCD_CAP_STATE_COUNT; new++) { - if (!strcasecmp(argv[2], ccd_cap_state_names[new])) - break; - } - if (new == CCD_CAP_STATE_COUNT) - return EC_ERROR_PARAM2; - - /* Get current state */ - old = raw_get_cap(cap, 1); - - /* If we're only unlocked, can't change to/from IfOpened */ - if (ccd_state == CCD_STATE_UNLOCKED && - (new == CCD_CAP_STATE_IF_OPENED || old == CCD_CAP_STATE_IF_OPENED)) - return EC_ERROR_ACCESS_DENIED; - - /* Set new state */ - return ccd_set_cap(cap, new); -} - -static int do_ccd_password(char *password) -{ - /* Only works if unlocked or opened */ - if (ccd_state == CCD_STATE_LOCKED) - return EC_ERROR_ACCESS_DENIED; - - if (raw_has_password()) { - const char clear_prefix[] = {'c', 'l', 'e', 'a', 'r', ':'}; - - /* - * The only allowed action at this point is to clear the - * password. To do it the user is supposed to enter - * 'clear:<passwd>' - */ - if (strncasecmp(password, clear_prefix, sizeof(clear_prefix))) - return EC_ERROR_ACCESS_DENIED; - - if (raw_check_password(password + sizeof(clear_prefix)) != - EC_SUCCESS) - return EC_ERROR_ACCESS_DENIED; - - return ccd_reset_password(); - } - - /* Set new password */ - return ccd_set_password(password); -} - -/* - * Common wrapper for CCD commands which are passed through the TPM task - * context. - * - * All commands could have a single parameter, which is the password (to be - * set, cleared, or entered to open/unlock). If argc value exceeds 1, the - * pointer to password is set, it is checked not to exceed maximum size. - * - * If the check succeeds, prepare a message containing a TPM vendor command, - * have the TPM task process the message and report the result to the caller. - * - * Message header is always the same, the payload is the password, if - * supplied. - * - * Expected output is nothing on success, or a single byte EC return code. - */ -static int ccd_command_wrapper(int argc, char *password, - enum ccd_vendor_subcommands subcmd) -{ - uint8_t buf[sizeof(struct ccd_vendor_cmd_header) + - CCD_MAX_PASSWORD_SIZE]; - struct ccd_vendor_cmd_header *vch = (struct ccd_vendor_cmd_header *)buf; - size_t password_size = 0; - uint32_t return_code; - - if (argc > 1) { - password_size = strlen(password); - if (password_size > CCD_MAX_PASSWORD_SIZE) - return EC_ERROR_PARAM1; - } - - /* Build the extension command to set/clear CCD password. */ - vch->tpm_header.tag = htobe16(0x8001); /* TPM_ST_NO_SESSIONS */ - vch->tpm_header.size = htobe32(sizeof(*vch) + password_size); - vch->tpm_header.command_code = htobe32(TPM_CC_VENDOR_BIT_MASK); - vch->tpm_header.subcommand_code = htobe16(VENDOR_CC_CCD); - vch->ccd_subcommand = subcmd; - - memcpy(vch + 1, password, password_size); - tpm_alt_extension(&vch->tpm_header, sizeof(buf)); - - /* - * Return status in the command code field now, in case of error, - * error code is the first byte after the header. - */ - return_code = be32toh(vch->tpm_header.command_code); - if ((return_code != VENDOR_RC_SUCCESS) && - (return_code != (VENDOR_RC_IN_PROGRESS|VENDOR_RC_ERR))) { - return vch->ccd_subcommand; - } - return EC_SUCCESS; -} - -static enum vendor_cmd_rc ccd_open(struct vendor_cmd_params *p) -{ - int is_long = 1; - int need_pp = 1; - int rv; - char *buffer = p->buffer; - const char *why_denied = "forced"; - - if (force_disabled) - goto denied; - - if (ccd_state == CCD_STATE_OPENED) - return VENDOR_RC_SUCCESS; - - /* FWMP blocks open even if a password is set */ - if (!board_fwmp_allows_unlock()) { - why_denied = "fwmp"; - goto denied; - } - - /* Make sure open is allowed */ - if (raw_has_password()) { - /* Open allowed if correct password is specified */ - - if (!p->in_size) { - /* ...which it wasn't */ - p->out_size = 1; - buffer[0] = EC_ERROR_PARAM_COUNT; - return VENDOR_RC_PASSWORD_REQUIRED; - } - - /* - * We know there is plenty of room in the TPM buffer this is - * stored in. - */ - buffer[p->in_size] = '\0'; - rv = raw_check_password(buffer); - if (rv) { - p->out_size = 1; - buffer[0] = rv; - return VENDOR_RC_INTERNAL_ERROR; - } - } else if (!board_battery_is_present()) { - /* Open allowed with no password if battery is removed */ - } else if ((ccd_is_cap_enabled(CCD_CAP_OPEN_WITHOUT_DEV_MODE) || - (board_vboot_dev_mode_enabled())) && - (ccd_is_cap_enabled(CCD_CAP_OPEN_FROM_USB) || - !(p->flags & VENDOR_CMD_FROM_USB))) { - /* - * Open allowed with no password if dev mode enabled and - * command came from the AP. CCD capabilities can be used to - * bypass these checks. - */ - } else { - /* - * - Battery is present - * - Either not in developer mode or the command came from USB - */ - why_denied = "open from AP in devmode or remove batt"; - goto denied; - } - - /* Fail and abort if already checking physical presence */ - if (physical_detect_busy()) { - physical_detect_abort(); - p->out_size = 1; - buffer[0] = EC_ERROR_BUSY; - return VENDOR_RC_INTERNAL_ERROR; - } - - /* Reduce physical presence if enabled via config */ - if (ccd_is_cap_enabled(CCD_CAP_OPEN_WITHOUT_LONG_PP)) - is_long = 0; - if (!is_long && ccd_is_cap_enabled(CCD_CAP_UNLOCK_WITHOUT_SHORT_PP)) - need_pp = 0; - - /* Bypass physical presence check entirely if battery is removed */ - if (ccd_is_cap_enabled(CCD_CAP_REMOVE_BATTERY_BYPASSES_PP) && - !board_battery_is_present()) { - need_pp = 0; - } - - if (need_pp) { - /* Start physical presence detect */ - ccprintf("Starting CCD open...\n"); - rv = physical_detect_start(is_long, ccd_open_done_async); - if (rv != EC_SUCCESS) { - p->out_size = 1; - buffer[0] = rv; - return VENDOR_RC_INTERNAL_ERROR; - } - return VENDOR_RC_IN_PROGRESS; - } - - /* No physical presence required; go straight to done */ - ccd_open_done(1); - - return VENDOR_RC_SUCCESS; - -denied: - /* Open not allowed for some reason */ - CPRINTS("%s denied: %s", __func__, why_denied); - p->out_size = 1; - buffer[0] = EC_ERROR_ACCESS_DENIED; - return VENDOR_RC_NOT_ALLOWED; -} - -static enum vendor_cmd_rc ccd_unlock(struct vendor_cmd_params *p) -{ - int need_pp = 1; - int rv; - char *buffer = p->buffer; - - if (force_disabled) { - p->out_size = 1; - buffer[0] = EC_ERROR_ACCESS_DENIED; - return VENDOR_RC_NOT_ALLOWED; - } - - if (ccd_state == CCD_STATE_UNLOCKED) - return VENDOR_RC_SUCCESS; - - /* Can go from opened to unlocked with no delay or password */ - if (ccd_state == CCD_STATE_OPENED) { - ccd_unlock_done(); - return VENDOR_RC_SUCCESS; - } - - /* Only allowed if password is already set, and not blocked by FWMP */ - if (!raw_has_password() || !board_fwmp_allows_unlock()) { - p->out_size = 1; - buffer[0] = EC_ERROR_ACCESS_DENIED; - return VENDOR_RC_NOT_ALLOWED; - } - - /* Make sure password was specified */ - if (!p->in_size) { - p->out_size = 1; - buffer[0] = EC_ERROR_PARAM_COUNT; - return VENDOR_RC_PASSWORD_REQUIRED; - } - - /* - * Check the password. We know there is plenty of room in the TPM - * buffer this is stored in. - */ - buffer[p->in_size] = '\0'; - rv = raw_check_password(buffer); - if (rv) { - p->out_size = 1; - buffer[0] = rv; - return VENDOR_RC_INTERNAL_ERROR; - } - - /* Fail and abort if already checking physical presence */ - if (physical_detect_busy()) { - physical_detect_abort(); - p->out_size = 1; - buffer[0] = EC_ERROR_BUSY; - return VENDOR_RC_INTERNAL_ERROR; - } - - /* Bypass physical presence check if configured to do so */ - if (ccd_is_cap_enabled(CCD_CAP_UNLOCK_WITHOUT_SHORT_PP)) - need_pp = 0; - - /* Bypass physical presence check entirely if battery is removed */ - if (ccd_is_cap_enabled(CCD_CAP_REMOVE_BATTERY_BYPASSES_PP) && - !board_battery_is_present()) { - need_pp = 0; - } - - if (need_pp) { - /* Start physical presence detect */ - ccprintf("Starting CCD unlock...\n"); - rv = physical_detect_start(0, ccd_unlock_done); - if (rv != EC_SUCCESS) { - p->out_size = 1; - buffer[0] = rv; - return VENDOR_RC_INTERNAL_ERROR; - } - return VENDOR_RC_IN_PROGRESS; - } - - /* Unlock immediately */ - ccd_unlock_done(); - - return VENDOR_RC_SUCCESS; -} - -static enum vendor_cmd_rc ccd_lock(struct vendor_cmd_params *p) -{ - /* Lock always works */ - ccprintf("CCD locked.\n"); - ccd_set_state(CCD_STATE_LOCKED); - return VENDOR_RC_SUCCESS; -} - - - -/* NOTE: Testlab command is console-only; no TPM vendor command for this */ -static int command_ccd_testlab(int argc, char **argv) -{ - int newflag = 0; - - if (force_disabled) - return EC_ERROR_ACCESS_DENIED; - - if (argc < 2) { - ccprintf("CCD test lab mode %sbled\n", - ccd_get_flag(CCD_FLAG_TEST_LAB) ? "ena" : "disa"); - return EC_SUCCESS; - } - - if (!strcasecmp(argv[1], "open")) { - if (!ccd_get_flag(CCD_FLAG_TEST_LAB)) - return EC_ERROR_ACCESS_DENIED; - - /* Go directly to open state without wiping TPM or rebooting */ - ccd_set_state(CCD_STATE_OPENED); - return EC_SUCCESS; - } - - /* All other commands require CCD opened */ - if (ccd_state != CCD_STATE_OPENED) - return EC_ERROR_ACCESS_DENIED; - - if (!parse_bool(argv[1], &newflag)) - return EC_ERROR_PARAM1; - - if (newflag == ccd_get_flag(CCD_FLAG_TEST_LAB)) - return EC_SUCCESS; /* No change */ - - /* If we're still here, need to toggle test lab flag */ - ccprintf("Requesting change of test lab flag.\n"); - if (newflag) - ccprintf("NOTE: THIS WILL MAKE THIS DEVICE INSECURE!!!\n"); - return physical_detect_start(0, ccd_testlab_toggle); -} - -#ifdef CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE -/** - * Test command to forcibly reset CCD config - */ -static int command_ccd_oops(void) -{ - /* Completely reset CCD config and go to opened state */ - force_disabled = 0; - ccprintf("Aborting physical detect...\n"); - physical_detect_abort(); - ccprintf("Resetting CCD config...\n"); - ccd_reset_config(CCD_RESET_TEST_LAB); - ccprintf("Opening CCD...\n"); - ccd_set_state(CCD_STATE_OPENED); - return EC_SUCCESS; -} -#endif /* CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE */ - -#ifdef CONFIG_CMD_CCD_DISABLE -static int command_ccd_disable(void) -{ - ccd_disable(); - return EC_SUCCESS; -} -#endif /* CONFIG_CMD_CCD_DISABLE */ - -static int command_ccd_help(void) -{ - int i; - - ccputs("usage: ccd [cmd [args]]\n\n" - "get (or just 'ccd')\n" - "\tPrint current config\n\n" - "lock\n" - "unlock [password]\n" - "open [password]\n" - "\tSet CCD state\n\n" - "set <capability> ["); - cflush(); - - for (i = 0; i < CCD_CAP_STATE_COUNT; i++) - ccprintf("%s%s", i ? " | " : "", ccd_cap_state_names[i]); - ccputs("]\n" - "\tSet capability to state\n\n" - "password [<new password> | clear]\n" - "\tSet or clear CCD password\n\n" - "reset [factory]\n" - "\tReset CCD config\n\n" - "testlab [enable | disable | open]\n" - "\tToggle testlab mode or force CCD open\n\n"); - cflush(); - -#ifdef CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE - ccputs("oops\n" - "\tForce-reset CCD config\n\n"); -#endif -#ifdef CONFIG_CMD_CCD_DISABLE - ccputs("disable\n" - "\tTemporarily disable CCD\n\n"); -#endif - - return EC_SUCCESS; -} - -/** - * Case closed debugging config command. - */ -static int command_ccd_body(int argc, char **argv) -{ - /* If no args or 'get', print info */ - if (argc < 2 || !strcasecmp(argv[1], "get")) - return command_ccd_info(); - - /* Check test lab command first */ - if (!strcasecmp(argv[1], "testlab")) - return command_ccd_testlab(argc - 1, argv + 1); - - /* Commands to set state */ - if (!strcasecmp(argv[1], "lock")) - return ccd_command_wrapper(0, NULL, CCDV_LOCK); - if (!strcasecmp(argv[1], "unlock")) { - if (!raw_has_password()) { - ccprintf("Unlock only allowed after password is set\n"); - return EC_ERROR_ACCESS_DENIED; - } - return ccd_command_wrapper(argc - 1, argv[2], CCDV_UNLOCK); - } - if (!strcasecmp(argv[1], "open")) - return ccd_command_wrapper(argc - 1, argv[2], CCDV_OPEN); - - /* Commands to configure capabilities */ - if (!strcasecmp(argv[1], "set")) - return command_ccd_set(argc - 1, argv + 1); - if (!strcasecmp(argv[1], "password")) { - if (argc != 3) - return EC_ERROR_PARAM_COUNT; - return ccd_command_wrapper(argc - 1, argv[2], CCDV_PASSWORD); - } - if (!strcasecmp(argv[1], "reset")) - return command_ccd_reset(argc - 1, argv + 1); - - /* Optional commands */ -#ifdef CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE - if (!strcasecmp(argv[1], "oops")) - return command_ccd_oops(); -#endif -#ifdef CONFIG_CMD_CCD_DISABLE - if (!strcasecmp(argv[1], "disable")) - return command_ccd_disable(); -#endif - - /* Anything else (including "help") prints help */ - return command_ccd_help(); -} - -static int command_ccd(int argc, char **argv) -{ - int rv; - - ccd_console_active = 1; - rv = command_ccd_body(argc, argv); - ccd_console_active = 0; - - return rv; -} -DECLARE_SAFE_CONSOLE_COMMAND(ccd, command_ccd, - "[help | ...]", - "Configure case-closed debugging"); - -/* - * Handle the CCVD_PASSWORD subcommand. - * - * The payload of the command is a text string to use to set or clear the - * password. - */ -static enum vendor_cmd_rc ccd_password(struct vendor_cmd_params *p) -{ - int rv = EC_SUCCESS; - char password[CCD_MAX_PASSWORD_SIZE + 1]; - char *response = p->buffer; - - /* - * Only allow setting a password from the AP, not USB. This increases - * the effort required for an attacker to set one externally, even if - * they have access to a system someone left in the opened state. - * - * An attacker can still set testlab mode or open up the CCD config, - * but those changes are reversible by the device owner. - */ - if (p->flags & VENDOR_CMD_FROM_USB) { - p->out_size = 1; - *response = EC_ERROR_ACCESS_DENIED; - return VENDOR_RC_NOT_ALLOWED; - } - - if (!p->in_size || (p->in_size >= sizeof(password))) { - rv = EC_ERROR_PARAM1; - } else { - memcpy(password, p->buffer, p->in_size); - password[p->in_size] = '\0'; - rv = do_ccd_password(password); - always_memset(password, 0, p->in_size); - } - - if (rv != EC_SUCCESS) { - *response = rv; - p->out_size = 1; - return VENDOR_RC_INTERNAL_ERROR; - } - - return VENDOR_RC_SUCCESS; -} - -static enum vendor_cmd_rc ccd_pp_poll(struct vendor_cmd_params *p) -{ - char *buffer = p->buffer; - - if ((ccd_state == CCD_STATE_OPENED) || - (ccd_state == CCD_STATE_UNLOCKED)) { - buffer[0] = CCD_PP_DONE; - } else { - switch (physical_presense_fsm_state()) { - case PP_AWAITING_PRESS: - buffer[0] = CCD_PP_AWAITING_PRESS; - break; - case PP_BETWEEN_PRESSES: - buffer[0] = CCD_PP_BETWEEN_PRESSES; - break; - default: - buffer[0] = CCD_PP_CLOSED; - break; - } - } - p->out_size = 1; - return VENDOR_RC_SUCCESS; -} - -static enum vendor_cmd_rc ccd_pp_poll_unlock(struct vendor_cmd_params *p) -{ - char *buffer = p->buffer; - - if ((ccd_state != CCD_STATE_OPENED) && - (ccd_state != CCD_STATE_UNLOCKED)) - return ccd_pp_poll(p); - - p->out_size = 1; - buffer[0] = CCD_PP_DONE; - - return VENDOR_RC_SUCCESS; -} - -static enum vendor_cmd_rc ccd_pp_poll_open(struct vendor_cmd_params *p) -{ - char *buffer = p->buffer; - - if (ccd_state != CCD_STATE_OPENED) - return ccd_pp_poll(p); - - p->out_size = 1; - buffer[0] = CCD_PP_DONE; - - return VENDOR_RC_SUCCESS; -} - -static enum vendor_cmd_rc ccd_get_info(struct vendor_cmd_params *p) -{ - int i; - struct ccd_info_response response = {}; - - for (i = 0; i < CCD_CAP_COUNT; i++) { - int index; - int shift; - - /* Each capability takes 2 bits. */ - index = i / (32 / CCD_CAP_BITS); - shift = (i % (32 / CCD_CAP_BITS)) * CCD_CAP_BITS; - response.ccd_caps_current[index] |= raw_get_cap(i, 1) << shift; - response.ccd_caps_defaults[index] |= - cap_info[i].default_state << shift; - } - - response.ccd_flags = htobe32(raw_get_flags()); - response.ccd_state = ccd_get_state(); - response.ccd_indicator_bitmap = raw_has_password() ? - CCD_INDICATOR_BIT_HAS_PASSWORD : 0; - response.ccd_indicator_bitmap |= raw_check_all_caps_default() ? - CCD_INDICATOR_BIT_ALL_CAPS_DEFAULT : 0; - response.ccd_force_disabled = force_disabled; - for (i = 0; i < ARRAY_SIZE(response.ccd_caps_current); i++) { - response.ccd_caps_current[i] = - htobe32(response.ccd_caps_current[i]); - response.ccd_caps_defaults[i] = - htobe32(response.ccd_caps_defaults[i]); - } - - p->out_size = sizeof(response); - memcpy(p->buffer, &response, sizeof(response)); - - return VENDOR_RC_SUCCESS; -} - -/* - * Common TPM Vendor command handler used to demultiplex various CCD commands - * which need to be available both throuh CLI and over /dev/tpm0. - */ -static enum vendor_cmd_rc ccd_vendor(struct vendor_cmd_params *p) -{ - enum vendor_cmd_rc (*handler)(struct vendor_cmd_params *p); - enum vendor_cmd_rc rc; - - /* - * The command buffer points to the next byte after tpm header, i.e. to - * the CCD subcommand. Cache the pointer to make it easier to access - * and manipulate. - */ - char *buffer = p->buffer; - - /* - * Make sure the buffer is large enough to accommodate any CCD - * subcommand response (plus one byte, since the response is shifted), - * so we can skip size checks in the processing functions. - */ - if (p->out_size < sizeof(struct ccd_info_response) + 1) { - p->out_size = 0; - return VENDOR_RC_RESPONSE_TOO_BIG; - } - - /* Now we can assume no output data unless proven otherwise */ - p->out_size = 0; - - /* Pick what to do based on subcommand. */ - switch (buffer[0]) { - case CCDV_PASSWORD: - handler = ccd_password; - break; - - case CCDV_OPEN: - handler = ccd_open; - break; - - case CCDV_UNLOCK: - handler = ccd_unlock; - break; - - case CCDV_LOCK: - handler = ccd_lock; - break; - - case CCDV_PP_POLL_UNLOCK: - handler = ccd_pp_poll_unlock; - break; - - case CCDV_PP_POLL_OPEN: - handler = ccd_pp_poll_open; - break; - - case CCDV_GET_INFO: - handler = ccd_get_info; - break; - - default: - CPRINTS("%s:%d - unknown subcommand", __func__, __LINE__); - return VENDOR_RC_NO_SUCH_SUBCOMMAND; - } - - /* Shift buffer past the subcommand when calling the handler */ - p->buffer = buffer + 1; - p->in_size--; - rc = handler(p); - p->buffer = buffer; - p->in_size++; - - /* - * Move response up for the master to see it in the right - * place in the response buffer. We have to do this because the - * first byte of the buffer on input was the subcommand, so we - * passed buffer + 1 in the handler call above. - */ - memmove(buffer, buffer + 1, p->out_size); - return rc; -} -DECLARE_VENDOR_COMMAND_P(VENDOR_CC_CCD, ccd_vendor); - -static enum vendor_cmd_rc ccd_disable_factory_mode(enum vendor_cmd_cc code, - void *buf, - size_t input_size, - size_t *response_size) -{ - int rv = EC_SUCCESS; - int error_line; - - do { - if (raw_has_password()) { - error_line = __LINE__; - rv = EC_ERROR_ACCESS_DENIED; - break; - } - - /* Check if physical presence is required to unlock. */ - if (!ccd_is_cap_enabled(CCD_CAP_REMOVE_BATTERY_BYPASSES_PP) || - board_battery_is_present()) { - const uint8_t required_capabilities[] = { - CCD_CAP_OPEN_WITHOUT_TPM_WIPE, - CCD_CAP_UNLOCK_WITHOUT_AP_REBOOT, - CCD_CAP_OPEN_WITHOUT_LONG_PP, - CCD_CAP_UNLOCK_WITHOUT_SHORT_PP - }; - unsigned int i; - - for (i = 0; - i < ARRAY_SIZE(required_capabilities); - i++) { - if (!ccd_is_cap_enabled - (required_capabilities[i])) - break; - } - - if (i < ARRAY_SIZE(required_capabilities)) { - CPRINTF("Capability %d is not present\n", - required_capabilities[i]); - error_line = __LINE__; - rv = EC_ERROR_ACCESS_DENIED; - break; - } - } - - ccd_set_state(CCD_STATE_OPENED); - - rv = command_ccd_reset(0, NULL); - if (rv != EC_SUCCESS) { - error_line = __LINE__; - break; - } - - - ccd_lock(NULL); - - /* - * We do it here to make sure that the device comes out of - * factory mode with WP enabled, but in general CCD reset needs - * to enforce WP state. - * - * TODO(rspangler): sort out CCD state and WP correlation, - * b/73075443. - */ - board_wp_follow_ccd_config(); - - /* - * Use raw_set_flag() because the factory mode flag is internal - */ - mutex_lock(&ccd_config_mutex); - raw_set_flag(CCD_FLAG_FACTORY_MODE_ENABLED, 0); - mutex_unlock(&ccd_config_mutex); - - *response_size = 0; - return VENDOR_RC_SUCCESS; - } while (0); - - CPRINTF("%s: error in line %d\n", __func__, error_line); - - ((uint8_t *)buf)[0] = (uint8_t)rv; - *response_size = 1; - return VENDOR_RC_INTERNAL_ERROR; -} -DECLARE_VENDOR_COMMAND(VENDOR_CC_DISABLE_FACTORY, ccd_disable_factory_mode); |