/* 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; /* 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); } ccd_config_loaded = 1; return; } /* 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); } 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; rv = writevars(); /* Notify CCD users of configuration change */ 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; } 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); /* 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); set_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; 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(); 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); } /******************************************************************************/ /* 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: %.8h\n", config.capabilities); 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:' */ 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 { /* * - Password not set * - Battery is present * - Either not in developer mode or the command came from USB */ why_denied = "nopwd"; 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 ["); 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 [ | 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\n", __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. */ set_wp_follow_ccd_config(); *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);