/* 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 "case_closed_debug.h" #include "common.h" #include "console.h" #include "cryptoc/sha256.h" #include "dcrypto.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 "trng.h" #define CPRINTS(format, args...) cprints(CC_CCD, format, ## args) #define CPRINTF(format, args...) cprintf(CC_CCD, format, ## args) /* 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 /* Capability states */ enum ccd_capability_state { /* Default value */ CCD_CAP_STATE_DEFAULT = 0, /* Always available (state >= CCD_STATE_LOCKED) */ CCD_CAP_STATE_ALWAYS = 1, /* Unless locked (state >= CCD_STATE_UNLOCKED) */ CCD_CAP_STATE_UNLESS_LOCKED = 2, /* Only if opened (state >= CCD_STATE_OPENED) */ CCD_CAP_STATE_IF_OPENED = 3, /* Number of capability states */ CCD_CAP_STATE_COUNT }; /* Size of password salt and digest in bytes */ #define CCD_PASSWORD_SALT_SIZE 4 #define CCD_PASSWORD_DIGEST_SIZE 16 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]; }; struct ccd_capability_info { /* Capability name */ const char *name; /* Default state, if config set to CCD_CAP_STATE_DEFAULT */ enum ccd_capability_state default_state; }; /* Flags for ccd_reset_config() */ enum ccd_reset_config_flags { /* Also reset test lab flag */ CCD_RESET_TEST_LAB = (1 << 0), /* Only reset Always/UnlessLocked settings */ CCD_RESET_UNLOCKED_ONLY = (1 << 1), /* Use RMA/factory defaults */ CCD_RESET_RMA = (1 << 2) }; /* Forward declarations of static functions */ static int ccd_reset_config(unsigned flags); /* 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] = { {"UartGscRxAPTx", CCD_CAP_STATE_ALWAYS}, {"UartGscTxAPRx", CCD_CAP_STATE_ALWAYS}, {"UartGscRxECTx", CCD_CAP_STATE_ALWAYS}, {"UartGscTxECRx", CCD_CAP_STATE_IF_OPENED}, {"FlashAP", CCD_CAP_STATE_IF_OPENED}, {"FlashEC", CCD_CAP_STATE_IF_OPENED}, {"OverrideWP", CCD_CAP_STATE_IF_OPENED}, {"RebootECAP", CCD_CAP_STATE_IF_OPENED}, {"GscFullConsole", CCD_CAP_STATE_IF_OPENED}, {"UnlockNoReboot", CCD_CAP_STATE_ALWAYS}, {"UnlockNoShortPP", CCD_CAP_STATE_ALWAYS}, {"OpenNoTPMWipe", CCD_CAP_STATE_IF_OPENED}, {"OpenNoLongPP", CCD_CAP_STATE_IF_OPENED}, {"BatteryBypassPP", CCD_CAP_STATE_ALWAYS}, {"UpdateNoTPMWipe", CCD_CAP_STATE_ALWAYS}, {"I2C", CCD_CAP_STATE_IF_OPENED}, }; static const char *ccd_state_names[CCD_STATE_COUNT] = { "Locked", "Unlocked", "Opened"}; static const char *ccd_cap_state_names[CCD_CAP_STATE_COUNT] = { "Default", "Always", "UnlessLocked", "IfOpened"}; 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; /******************************************************************************/ /* 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) { int c = (config.capabilities[cap / 4] >> (2 * (cap % 4))) & 3; 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) { config.capabilities[cap / 4] &= ~(3 << (2 * (cap % 4))); config.capabilities[cap / 4] |= (state & 3) << (2 * (cap % 4)); } /** * 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 */ #ifdef CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE /* TODO(rspangler): remove when we wire this up to real capabilities */ void test_ccd_change_hook(void) { CPRINTS("CCD change hook called"); } DECLARE_HOOK(HOOK_CCD_CHANGE, test_ccd_change_hook, HOOK_PRIO_FIRST); #endif /** * 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_TEST_LAB | CCD_RESET_RMA); } 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(); } /** * Reset CCD config to defaults. * * @param flags Reset flags (see enum ccd_reset_config_flags) * @return EC_SUCCESS, or non-zero if error. */ static int ccd_reset_config(unsigned 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_RMA) { /* Force RMA settings */ int i; /* Allow all capabilities all the time */ for (i = 0; i < CCD_CAP_COUNT; i++) { /* * Restricted console commands are still IfOpened, but * that's kinda meaningless because we set a * well-defined password below. */ if (i == CCD_CAP_GSC_RESTRICTED_CONSOLE) continue; 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); } /* 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 */ static void ccd_open_done(void) { if (!ccd_is_cap_enabled(CCD_CAP_OPEN_WITHOUT_TPM_WIPE)) { /* Can't open unless wipe succeeds */ if (board_wipe_tpm() != EC_SUCCESS) { CPRINTS("CCD open TPM wipe failed"); return; } } if (!ccd_is_cap_enabled(CCD_CAP_UNLOCK_WITHOUT_AP_REBOOT)) board_reboot_ap(); CPRINTS("CCD opened"); ccd_set_state(CCD_STATE_OPENED); } 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" : "dis"); else CPRINTS("Error setting CCD test lab mode!"); } /******************************************************************************/ /* External interface */ 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(); } return EC_SUCCESS; } static int command_ccd_reset(int argc, char **argv) { int flags = 0; if (argc > 1) { if (!strcasecmp(argv[1], "rma")) flags = CCD_RESET_RMA; else return EC_ERROR_PARAM1; } switch (ccd_state) { case CCD_STATE_OPENED: ccprintf("%sResetting all settings.\n", flags & CCD_RESET_RMA ? "RMA " : ""); /* 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 command_ccd_password(int argc, char **argv) { /* Only works if unlocked or opened */ if (ccd_state == CCD_STATE_LOCKED) return EC_ERROR_ACCESS_DENIED; if (argc < 2) return EC_ERROR_PARAM_COUNT; /* If password was set from Opened, can't change if just Unlocked */ if (raw_has_password() && ccd_state == CCD_STATE_UNLOCKED && !ccd_get_flag(CCD_FLAG_PASSWORD_SET_WHEN_UNLOCKED)) return EC_ERROR_ACCESS_DENIED; if (!strcasecmp(argv[1], "clear")) return ccd_reset_password(); /* Set new password */ return ccd_set_password(argv[1]); } static int command_ccd_open(int argc, char **argv) { int is_long = 1; int need_pp = 1; int rv; if (force_disabled) return EC_ERROR_ACCESS_DENIED; if (ccd_state == CCD_STATE_OPENED) return EC_SUCCESS; if (raw_has_password()) { if (argc < 2) return EC_ERROR_PARAM_COUNT; rv = raw_check_password(argv[1]); if (rv) return rv; } else if (!board_fwmp_allows_unlock()) { return EC_ERROR_ACCESS_DENIED; } /* Fail and abort if already checking physical presence */ if (physical_detect_busy()) { physical_detect_abort(); return EC_ERROR_BUSY; } /* 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"); return physical_detect_start(is_long, ccd_open_done); } else { /* No physical presence required; go straight to done */ ccd_open_done(); return EC_SUCCESS; } } static int command_ccd_unlock(int argc, char **argv) { int need_pp = 1; int rv; if (force_disabled) return EC_ERROR_ACCESS_DENIED; if (ccd_state == CCD_STATE_UNLOCKED) return EC_SUCCESS; /* Can go from opened to unlocked with no delay or password */ if (ccd_state == CCD_STATE_OPENED) { ccd_unlock_done(); return EC_SUCCESS; } if (raw_has_password()) { if (argc < 2) return EC_ERROR_PARAM_COUNT; rv = raw_check_password(argv[1]); if (rv) return rv; } else if (!board_fwmp_allows_unlock()) { /* Unlock disabled by FWMP */ return EC_ERROR_ACCESS_DENIED; } else { /* * When unlock is requested via the console, physical presence * is required unless disabled by config. This prevents a * malicious peripheral from setitng a password. * * If this were a TPM vendor command from the AP, we would * instead check unlock restrictions based on the user login * state stored in ccd_unlock_restrict: * * 1) Unlock from the AP is unrestricted before any users * login, so enrollment policy scripts can update CCD config. * * 2) Owner accounts can unlock, but require physical presence * to prevent OS-level compromises from setting a password. * * 3) A non-owner account logging in blocks CCD config until * the next AP reboot, as implied by TPM reboot. */ } /* Fail and abort if already checking physical presence */ if (physical_detect_busy()) { physical_detect_abort(); return EC_ERROR_BUSY; } /* 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"); return physical_detect_start(0, ccd_unlock_done); } else { /* Unlock immediately */ ccd_unlock_done(); return EC_SUCCESS; } } static int command_ccd_lock(void) { /* Lock always works */ ccprintf("CCD locked.\n"); ccd_set_state(CCD_STATE_LOCKED); return EC_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", ccd_get_flag(CCD_FLAG_TEST_LAB) ? "ena" : "dis"); 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_CCDDISABLE static int command_ccd_disable(void) { ccd_disable(); return EC_SUCCESS; } #endif /* CONFIG_CMD_CCDDISABLE */ static int command_ccd_help(void) { int i; ccputs("usage: ccd [cmd [args]]\n" "\n" "lock\n" "unlock [password]\n" "open [password]\n" "\tSet CCD state\n" "set \n" "\tSet capability to state:"); cflush(); for (i = 0; i < CCD_CAP_STATE_COUNT; i++) ccprintf(" %s", ccd_cap_state_names[i]); ccputs("\n" "password [ | clear]\n" "\tSet or clear CCD password\n" "reset [rma]\n" "\tReset CCD config\n" "testlab [enable | disable | open]\n" "\tToggle testlab mode or force CCD open\n"); cflush(); #ifdef CONFIG_CASE_CLOSED_DEBUG_V1_UNSAFE ccputs("oops\n" "\tForce-reset CCD config\n"); #endif #ifdef CONFIG_CMD_CCDDISABLE ccputs("disable\n" "\tTemporarily disable CCD\n"); #endif return EC_SUCCESS; } /** * Case closed debugging config command. */ static int command_ccd(int argc, char **argv) { /* If no args, print info */ if (argc < 2) 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 command_ccd_lock(); if (!strcasecmp(argv[1], "unlock")) return command_ccd_unlock(argc - 1, argv + 1); if (!strcasecmp(argv[1], "open")) return command_ccd_open(argc - 1, argv + 1); /* Commands to configure capabilities */ if (!strcasecmp(argv[1], "set")) return command_ccd_set(argc - 1, argv + 1); if (!strcasecmp(argv[1], "password")) return command_ccd_password(argc - 1, argv + 1); 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_CCDDISABLE if (!strcasecmp(argv[1], "disable")) return command_ccd_disable(); #endif /* Anything else (including "help") prints help */ return command_ccd_help(); } DECLARE_SAFE_CONSOLE_COMMAND(ccd, command_ccd, "[help | ...]", "Configure case-closed debugging");