/* Copyright 2013-2018 IBM Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef pr_fmt #define pr_fmt(fmt) "STB: " fmt #endif #include #include #include #include #include #include "secureboot.h" static const void* hw_key_hash = NULL; static size_t hw_key_hash_size; static bool secure_mode = false; static bool secure_init = false; static unsigned int level = PR_ERR; static struct { enum secureboot_version version; const char *compat; } secureboot_map[] = { { IBM_SECUREBOOT_V1, "ibm,secureboot-v1" }, { IBM_SECUREBOOT_SOFTROM, "ibm,secureboot-v1-softrom" }, { IBM_SECUREBOOT_V2, "ibm,secureboot-v2" }, }; static void secureboot_enforce(void) { /* Sanity check */ if (!secure_mode) return; /* * TODO: Ideally, the BMC should decide what security policy to apply * (power off, reboot, switch PNOR sides, etc). We may need to provide * extra info to BMC other than just abort. Terminate Immediate * Attention ? (TI) */ prlog(PR_EMERG, "secure mode enforced, aborting.\n"); abort(); } bool secureboot_is_compatible(struct dt_node *node, int *version, const char **compat) { int i; if (!node) return false; for (i = 0; i < ARRAY_SIZE(secureboot_map); i++) { if (dt_node_is_compatible(node, secureboot_map[i].compat)) { if (version) *version = secureboot_map[i].version; if (compat) *compat = secureboot_map[i].compat; return true; } } return false; } void secureboot_init(void) { struct dt_node *node; const char *hash_algo; const char *compat = NULL; int version; size_t size; node = dt_find_by_path(dt_root, "/ibm,secureboot"); if (!node) { prlog(PR_NOTICE, "secure boot not supported\n"); return; } if (!secureboot_is_compatible(node, &version, &compat)) { /** * @fwts-label SecureBootNotCompatible * @fwts-advice Compatible secureboot driver not found. Probably, * hostboot/mambo/skiboot has updated the * /ibm,secureboot/compatible without adding a driver that * supports it. */ prlog(PR_ERR, "%s FAILED, /ibm,secureboot not compatible.\n", __func__); return; } prlog(PR_DEBUG, "Found %s\n", compat); if (nvram_query_eq("force-secure-mode", "always")) { secure_mode = true; prlog(PR_NOTICE, "secure mode on (FORCED by nvram)\n"); } else { secure_mode = dt_has_node_property(node, "secure-enabled", NULL); prlog(PR_INFO, "secure mode %s\n", secure_mode ? "on" : "off"); } /* Use emergency log level only when secure mode is ON */ if (secure_mode) level = PR_EMERG; else level = PR_ERR; if (version == IBM_SECUREBOOT_V1 || version == IBM_SECUREBOOT_SOFTROM) { hash_algo = dt_prop_get(node, "hash-algo"); if (strcmp(hash_algo, "sha512")) { /** * @fwts-label HashAlgoInvalid * @fwts-advice Hash algorithm invalid, secureboot * containers version 1 requires sha512. If you're * running the latest POWER firmware, so probably there * is a bug in the device tree received from hostboot. */ prlog(level, "secureboot init FAILED, hash-algo=%s " "not supported\n", hash_algo); secureboot_enforce(); } hw_key_hash_size = SHA512_DIGEST_LENGTH; } else if (version == IBM_SECUREBOOT_V2) { hw_key_hash_size = dt_prop_get_u32(node, "hw-key-hash-size"); if (hw_key_hash_size == 0) { prlog(level, "hw-key-hash-size=%zd too short\n", hw_key_hash_size); secureboot_enforce(); } if (hw_key_hash_size > SHA512_DIGEST_LENGTH) { prlog(level, "hw-key-hash-size=%zd too big\n", hw_key_hash_size); secureboot_enforce(); } } else { prlog(level, "%s FAILED. /ibm,secureboot not supported", __func__); secureboot_enforce(); } hw_key_hash = dt_prop_get_def_size(node, "hw-key-hash", NULL, &size); if (!hw_key_hash) { prlog(level, "hw-key-hash not found\n"); secureboot_enforce(); } if (size != hw_key_hash_size) { prlog(level, "hw_key-hash wrong size %zd (expected=%zd)\n", size, hw_key_hash_size); secureboot_enforce(); } if (cvc_init()) secureboot_enforce(); secure_init = true; } int secureboot_verify(enum resource_id id, void *buf, size_t len) { const char *name; uint64_t log; int rc = -1; name = flash_map_resource_name(id); if (!name) { prlog(level, "container NOT VERIFIED, resource_id=%d " "unknown\n", id); secureboot_enforce(); return -1; } if (!secure_init) { prlog(level, "container NOT VERIFIED, resource_id=%d " "secureboot not yet initialized\n", id); secureboot_enforce(); return -1; } rc = call_cvc_verify(buf, len, hw_key_hash, hw_key_hash_size, &log); if (rc == OPAL_SUCCESS) { prlog(PR_NOTICE, "%s verified\n", name); } else if (rc == OPAL_PARTIAL) { /* * The value returned in log indicates what checking has * failed. Return codes defined in * /hostboot/src/include/securerom/status_codes.H */ prlog(level, "%s verification FAILED. log=0x%" PRIx64 "\n", name, be64_to_cpu(log)); secureboot_enforce(); } else if (rc == OPAL_PARAMETER) { prlog(level, "%s NOT VERIFIED, invalid param. buf=%p, " "len=%zd key-hash=%p hash-size=%zd\n", name, buf, len, hw_key_hash, hw_key_hash_size); secureboot_enforce(); } else if (rc == OPAL_UNSUPPORTED) { prlog(level, "%s NOT VERIFIED, CVC-verify service not " "supported\n", name); secureboot_enforce(); } else { prlog(level, "%s NOT VERIFIED, unknown CVC-verify error. " "rc=%d\n", name, rc); secureboot_enforce(); } return 0; }