diff options
author | Claudio Carvalho <cclaudio@linux.vnet.ibm.com> | 2016-09-28 05:01:30 -0300 |
---|---|---|
committer | Stewart Smith <stewart@linux.vnet.ibm.com> | 2016-10-10 15:29:36 +1100 |
commit | d15dd47d4a0f766282a034641621529e58ae8b25 (patch) | |
tree | 659ebfdd0e1e6c187b85f88827d468c751e23fc6 /libstb/stb.c | |
parent | 3837c6f0cf4cffb5de874413eed755c0c698b076 (diff) | |
download | talos-skiboot-d15dd47d4a0f766282a034641621529e58ae8b25.tar.gz talos-skiboot-d15dd47d4a0f766282a034641621529e58ae8b25.zip |
libstb: add secure and trusted boot interface
stb.c implements the libstb API, which is an API for secure and trusted boot:
- stb_init(): read secure mode and trusted mode from device tree and
load drivers accordingly
- tb_measure(): measure a resource downloaded from PNOR if trusted mode
is on. That is, an EV_ACTION event is recorded in the event log for
the mapped PCR and the sha1 and sha256 measurements are extended in
the mapped PCR.
- sb_verify(): verify the integrity and authenticity of a resource
downloaded from PNOR if secure mode is on. The boot process is aborted
if the verification fails.
- stb_final(): this is called to add marks to TPM and event log before
handover to petitboot kernel. Basically, it records an EV_SEPARATOR
event in the event log for PCR[0-7], extends the sha1 and sha256
digests of 0xFFFFFFFF in PCR[0-7], and deallocates the memory allocated
for secure and trusted boot.
For more information please refer to 'doc/stb.rst'.
Signed-off-by: Claudio Carvalho <cclaudio@linux.vnet.ibm.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'libstb/stb.c')
-rw-r--r-- | libstb/stb.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/libstb/stb.c b/libstb/stb.c new file mode 100644 index 00000000..6e1dcefd --- /dev/null +++ b/libstb/stb.c @@ -0,0 +1,305 @@ +/* Copyright 2013-2016 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. + */ + +#include <skiboot.h> +#include <device.h> +#include <platform.h> +#include <string.h> +#include <stdio.h> +#include "stb.h" +#include "status_codes.h" +#include "container.h" +#include "rom.h" +#include "tpm_chip.h" + +/* For debugging only */ +//#define STB_DEBUG +//#define STB_FORCE_SECURE_MODE +//#define STB_FORCE_TRUSTED_MODE + +static bool secure_mode = false; +static bool trusted_mode = false; + +static struct rom_driver_ops *rom_driver = NULL; + +#define MAX_RESOURCE_NAME 15 + +/* + * This maps a PCR for each resource we can measure. The PCR number is + * mapped according to the TCG PC Client Platform Firmware Profile + * specification, Revision 00.21 + * Only resources included in this whitelist can be measured. + */ +static struct { + + /* PNOR partition id */ + enum resource_id id; + + /* PCR mapping for the resource id */ + TPM_Pcr pcr; + + /* Resource name */ + const char name[MAX_RESOURCE_NAME+1]; + +} resource_map[] = { + { RESOURCE_ID_KERNEL, PCR_4, "BOOTKERNEL" }, + { RESOURCE_ID_CAPP, PCR_2, "CAPP"}, +}; + +struct event_hash { + const unsigned char *sha1; + const unsigned char *sha256; +}; + +/* + * Event Separator - digest of 0xFFFFFFFF + */ +static struct event_hash evFF = { + .sha1 = "\xd9\xbe\x65\x24\xa5\xf5\x04\x7d\xb5\x86" + "\x68\x13\xac\xf3\x27\x78\x92\xa7\xa3\x0a", + + .sha256 = "\xad\x95\x13\x1b\xc0\xb7\x99\xc0\xb1\xaf" + "\x47\x7f\xb1\x4f\xcf\x26\xa6\xa9\xf7\x60" + "\x79\xe4\x8b\xf0\x90\xac\xb7\xe8\x36\x7b" + "\xfd\x0e" +}; + +static int stb_resource_lookup(enum resource_id id) +{ + int i; + for (i = 0; i < ARRAY_SIZE(resource_map); i++) + if (resource_map[i].id == id) + return i; + return -1; +} + +static void sb_enforce(void) +{ + /* + * 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, "STB: Secure mode enforced, aborting.\n"); + abort(); +} + +void stb_init(void) +{ + const struct dt_node *ibm_secureboot; + /* + * The ibm,secureboot device tree properties are documented in + * 'doc/device-tree/ibm,secureboot.rst' + */ + ibm_secureboot = dt_find_by_path(dt_root, "/ibm,secureboot"); + if (ibm_secureboot == NULL) { + prlog(PR_NOTICE,"STB: secure and trusted boot not supported\n"); + return; + } + +#ifdef STB_FORCE_SECURE_MODE + secure_mode = true; + prlog(PR_NOTICE, "STB: secure mode on (forced!)\n"); +#else + secure_mode = dt_has_node_property(ibm_secureboot, "secure-enabled", + NULL); + prlog(PR_NOTICE, "STB: secure mode %s\n", + secure_mode ? "on" : "off"); +#endif + +#ifdef STB_FORCE_TRUSTED_MODE + trusted_mode = true; + prlog(PR_NOTICE, "STB: trusted mode on (forced!)\n"); +#else + trusted_mode = dt_has_node_property(ibm_secureboot, "trusted-enabled", + NULL); + prlog(PR_NOTICE, "STB: trusted mode %s\n", + trusted_mode ? "on" : "off"); +#endif + + if (!secure_mode && !trusted_mode) + return; + rom_driver = rom_init(ibm_secureboot); + if (secure_mode && !rom_driver) { + prlog(PR_EMERG, "STB: compatible romcode driver not found\n"); + sb_enforce(); + } + if (trusted_mode) + tpm_init(); +} + +int stb_final(void) +{ + uint32_t pcr; + int rc = 0; + + if (trusted_mode) { +#ifdef STB_DEBUG + prlog(PR_NOTICE, "STB: evFF.sha1:\n"); + stb_print_data((uint8_t*) evFF.sha1, TPM_ALG_SHA1_SIZE); + prlog(PR_NOTICE, "STB: evFF.sha256:\n"); + stb_print_data((uint8_t*) evFF.sha256, TPM_ALG_SHA256_SIZE); +#endif + /* + * We are done. Extending the digest of 0xFFFFFFFF + * in PCR[0-7], and recording an EV_SEPARATOR event in + * event log as defined in the TCG Platform Firmware Profile + * specification, Revision 00.21 + */ + for (pcr = 0; pcr < 8; pcr++) { + rc = tpm_extendl(pcr, TPM_ALG_SHA256, + (uint8_t*) evFF.sha256, + TPM_ALG_SHA256_SIZE, TPM_ALG_SHA1, + (uint8_t*) evFF.sha1, + TPM_ALG_SHA1_SIZE, EV_SEPARATOR, + "Skiboot Boot"); + if (rc) + return rc; + prlog(PR_NOTICE, "STB: 0xFFFFFFFF measured " + "to pcr%d\n", pcr); + } + tpm_add_status_property(); + } + if (rom_driver) { + rom_driver->cleanup(); + rom_driver = NULL; + } + tpm_cleanup(); + secure_mode = false; + trusted_mode = false; + return rc; +} + +int tb_measure(enum resource_id id, uint32_t subid, void *buf, size_t len) +{ + int rc, r; + uint8_t digest[SHA512_DIGEST_LENGTH]; + uint8_t* digestp; + + rc = 0; + digestp = NULL; + if (!trusted_mode) { + prlog(PR_NOTICE, "STB: %s skipped resource %d, " + "trusted_mode=0\n", __func__, id); + return STB_TRUSTED_MODE_DISABLED; + } + r = stb_resource_lookup(id); + if (r == -1) { + /** + * @fwts-label STBMeasureResourceNotMapped + * @fwts-advice The resource is not registered in the resource_map[] + * array, but it should be otherwise the resource cannot be + * measured if trusted mode is on. + */ + prlog(PR_ERR, "STB: %s failed, resource %d not mapped\n", + __func__, id); + return STB_ARG_ERROR; + } + if (!buf) { + /** + * @fwts-label STBNullResourceReceived + * @fwts-advice Null resource passed to tb_measure. This has + * come from the resource load framework and likely indicates a + * bug in the framework. + */ + prlog(PR_ERR, "STB: %s failed: resource %s%d, buf null\n", + __func__, resource_map[r].name, subid); + return STB_ARG_ERROR; + } + memset(digest, 0, SHA512_DIGEST_LENGTH); + /* + * In secure mode we can use the sw-payload-hash from the container + * header to measure the container payload. Otherwise we must calculate + * the hash of the container payload (if it's a container) or the image + * (if it's not a container) + */ + if (secure_mode && stb_is_container(buf, len)) { + digestp = (uint8_t*) stb_sw_payload_hash(buf, len); + memcpy(digest, digestp, TPM_ALG_SHA256_SIZE); + } else if (!secure_mode && stb_is_container(buf, len)) { + rom_driver->sha512( + (void*)((uint8_t*)buf + SECURE_BOOT_HEADERS_SIZE), + len - SECURE_BOOT_HEADERS_SIZE, digest); + prlog(PR_INFO, "STB: %s sha512 hash re-calculated\n", + resource_map[r].name); + } else { + rom_driver->sha512(buf, len, digest); + prlog(PR_INFO, "STB: %s sha512 hash calculated\n", + resource_map[r].name); + } +#ifdef STB_DEBUG + /* print the payload/image hash */ + prlog(PR_NOTICE, "STB: %s hash:\n", resource_map[r].name); + stb_print_data(digest, TPM_ALG_SHA256_SIZE); +#endif + /* + * Measure the resource. Since the ROM code doesn't provide a sha1 hash + * algorithm, the sha512 hash is truncated to match the size required + * by each PCR bank. + */ + rc = tpm_extendl(resource_map[r].pcr, + TPM_ALG_SHA256, digest, TPM_ALG_SHA256_SIZE, + TPM_ALG_SHA1, digest, TPM_ALG_SHA1_SIZE, + EV_ACTION, resource_map[r].name); + if (rc) + return rc; + prlog(PR_NOTICE, "STB: %s%d measured to pcr%d\n", resource_map[r].name, + subid, resource_map[r].pcr); + return 0; +} + +int sb_verify(enum resource_id id, uint32_t subid, void *buf, size_t len) +{ + int r; + const char *name = NULL; + + if (!secure_mode) { + prlog(PR_NOTICE, "STB: %s skipped resource %d, " + "secure_mode=0\n", __func__, id); + return STB_SECURE_MODE_DISABLED; + } + r = stb_resource_lookup(id); + if (r == -1) + /** + * @fwts-label STBVerifyResourceNotMapped + * @fwts-advice Unregistered resources can be verified, but not + * measured. The resource should be registered in the + * resource_map[] array, otherwise the resource cannot be + * measured if trusted mode is on. + */ + prlog(PR_WARNING, "STB: verifying the non-expected " + "resource %d/%d\n", id, subid); + else + name = resource_map[r].name; + if (!rom_driver || !rom_driver->verify) { + prlog(PR_EMERG, "STB: secure boot not initialized\n"); + sb_enforce(); + } + if (!buf || len < SECURE_BOOT_HEADERS_SIZE) { + prlog(PR_EMERG, "STB: %s arg error: id %d/%d, buf %p, len %zd\n", + __func__, id, subid, buf, len); + sb_enforce(); + } + if (rom_driver->verify(buf)) { + prlog(PR_EMERG, "STB: %s failed: resource %s%d, " + "eyecatcher 0x%016llx\n", __func__, name, subid, + *((uint64_t*)buf)); + sb_enforce(); + } + prlog(PR_NOTICE, "STB: %s%d verified\n", name, subid); + return 0; +} |