[Skiboot] [PATCH 15/15] Add libstb
Claudio Carvalho
cclaudio at linux.vnet.ibm.com
Sat Sep 24 05:49:50 AEST 2016
On 09/20/2016 05:39 AM, Stewart Smith wrote:
> Claudio Carvalho <cclaudio at linux.vnet.ibm.com> writes:
>> Libstb implements 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
>> the control is passed 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.txt'.
>>
>> Signed-off-by: Claudio Carvalho <cclaudio at linux.vnet.ibm.com>
>> ---
>> libstb/Makefile.inc | 2 +-
>> libstb/status_codes.h | 2 +
>> libstb/stb.c | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> libstb/stb.h | 74 +++++++++++
>> 4 files changed, 410 insertions(+), 1 deletion(-)
>> create mode 100644 libstb/stb.c
>> create mode 100644 libstb/stb.h
>>
>> diff --git a/libstb/Makefile.inc b/libstb/Makefile.inc
>> index d395631..a88ac0d 100644
>> --- a/libstb/Makefile.inc
>> +++ b/libstb/Makefile.inc
>> @@ -4,7 +4,7 @@ LIBSTB_DIR = libstb
>>
>> SUBDIRS += $(LIBSTB_DIR)
>>
>> -LIBSTB_SRCS = container.c tpm.c rom.c
>> +LIBSTB_SRCS = stb.c container.c tpm.c rom.c
>> LIBSTB_OBJS = $(LIBSTB_SRCS:%.c=%.o)
>> LIBSTB = $(LIBSTB_DIR)/built-in.o
>>
>> diff --git a/libstb/status_codes.h b/libstb/status_codes.h
>> index 106853c..e936e7e 100644
>> --- a/libstb/status_codes.h
>> +++ b/libstb/status_codes.h
>> @@ -23,9 +23,11 @@
>> #define STB_DRIVER_ERROR -2
>>
>> /* secure boot */
>> +#define STB_SECURE_MODE_DISABLED 100
>> #define STB_VERIFY_FAILED -100
>>
>> /* trusted boot */
>> +#define STB_TRUSTED_MODE_DISABLED 200
>> #define STB_EVENTLOG_FAILED -200
>> #define STB_PCR_EXTEND_FAILED -201
>>
>> diff --git a/libstb/stb.c b/libstb/stb.c
>> new file mode 100644
>> index 0000000..59279a0
>> --- /dev/null
>> +++ b/libstb/stb.c
>> @@ -0,0 +1,333 @@
>> +/* 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.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 specs.
>> + * 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"},
>> +};
>
> I wonder if this should be up in the platform code?
A similar array, part_name_map[], is defined in core/flash.c. Maybe we
can merge both structures and create an array somewhere that can be
consumed by anyone just by adding an "extern array" in include/platform.h.
Concern #1: in the current design, new records can be added only if we
know to what TPM PCR the resource must be extended. Implications: for
example, if we need to add a new record specific for FSP platforms in
the array, the development of the FSP feature would need to count in
some time to define the PCR, but secure and trusted boot are not
supported by FSP platforms yet.
We can always do some refactoring in the structure and in the libstb
code to resolve that (e.g. adding a field that indicates whether or not
the resource should be verified/measured).
Concern #2: AFAIK, currently skiboot downloads external code only from
PNOR. If we decide to download code from somewhere else should we create
a new array for the new resources or can we use the same array?
If we keep resource_map[] in stb.c, it seems that we will have more
control on the array and the development of other skiboot parts will be
less affected by it.
> RESOURCE_ID_INITRAMFS, currently only used on FSP platforms.
>
> Any thoughts on what would happen on a FSP platform with the additional
> LID(s) we need loading? (vpd and hostservices).
>
Libstb is platform independent, but platforms need to initialize it and
properly call the libstb API to verify and measure all resources
consumed by skiboot at boot time.
Currently, my patches add support only for habanero, but we can easily
change them to support all BMC platforms. We just need to initialize
libstb for all BMC platforms instead, since all of them use core/flash.c
to download resources from PNOR.
Secure and trusted boot support for FSP platforms would be a separate
patch set, assuming that it doesn't use core/flash.c to download
resources from PNOR. Additionally, the PNOR partitions consumed by FSP
platforms would have to be added to resource_map[] (assuming that the
partition doesn't have dynamic content such as NVRAM which has only
variable:value records).
>> +struct event_hash {
>> + const unsigned char *sha1;
>> + const unsigned char *sha256;
>> +};
>> +
>> +/*
>> + * Digests of 0xFFFFFFFF for the EV_SEPARATOR event
>> + */
>> +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)
>> +{
>> + /* FIXME: we want BMC to 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();
>> +}
>> +
>> +static void sb_init(const struct dt_node *node)
>> +{
>> +#ifdef STB_FORCE_SECURE_MODE
>> + secure_mode = true;
>> + prlog(PR_NOTICE, "STB: secure mode enabled (forced!)\n");
>> +#else
>> + secure_mode = dt_has_node_property(node, "secure-enabled", NULL);
>> + prlog(PR_NOTICE, "STB: secure mode %s\n",
>> + secure_mode ? "enabled" : "disabled");
>> +#endif
>> +
>> + rom_driver = rom_init(node);
>> +
>> + if (secure_mode && !rom_driver) {
>> + prlog(PR_EMERG, "STB: rom driver not found\n");
>> + sb_enforce();
>> + }
>> +}
>> +
>> +static void tb_init(const struct dt_node *node)
>> +{
>> +#ifdef STB_FORCE_TRUSTED_MODE
>> + trusted_mode = true;
>> + prlog(PR_NOTICE, "STB: trusted mode enabled (forced!)\n");
>> +#else
>> + trusted_mode = dt_has_node_property(node, "trusted-enabled", NULL);
>> + prlog(PR_NOTICE, "STB: trusted mode %s\n",
>> + trusted_mode ? "enabled" : "disabled");
>> +#endif
>> +
>> + if (trusted_mode) {
>> + tpm_init();
>> + }
>> +}
>> +
>> +void stb_init(void)
>> +{
>> + const struct dt_node *ibm_secureboot;
>> +
>> + /**
>> + * The ibm,secureboot device tree properties are documented in
>> + * 'doc/device-tree/ibm,secureboot.txt'
>> + */
>> + 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;
>> + }
>> +
>> + /* Initialize secure boot and trusted boot */
>> + sb_init(ibm_secureboot);
>> + tb_init(ibm_secureboot);
>> +}
>> +
>> +int stb_final(void)
>> +{
>> + uint32_t pcr;
>> + int rc = STB_SUCCESS;
>> +
>> + 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
>> + */
>> + for (pcr = 0; pcr < 8; pcr++) {
>> + rc = tpm_extendl(pcr, TPM_ALG_SHA1,
>> + (uint8_t*) evFF.sha1,
>> + TPM_ALG_SHA1_SIZE, TPM_ALG_SHA256,
>> + (uint8_t*) evFF.sha256,
>> + TPM_ALG_SHA256_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 = STB_SUCCESS;
>> + 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)) {
>> +
>> + digestp = (uint8_t*) stb_sw_payload_hash(buf);
>> + memcpy(digest, digestp, TPM_ALG_SHA256_SIZE);
>> +
>> + } else if (!secure_mode && stb_is_container(buf)) {
>> + rom_driver->sha512(
>> + (void*)((uint8_t*)buf + SECURE_BOOT_HEADERS_SIZE),
>> + len - SECURE_BOOT_HEADERS_SIZE, digest);
>> + } else {
>> + rom_driver->sha512(buf, len, digest);
>> + }
>> +
>> +#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 it */
>> + rc = tpm_extendl(resource_map[r].pcr,
>> + TPM_ALG_SHA1, digest, TPM_ALG_SHA1_SIZE,
>> + TPM_ALG_SHA256, digest, TPM_ALG_SHA256_SIZE,
>> + EV_ACTION, resource_map[r].name);
>> + if (rc)
>> + return rc;
>> +
>> + prlog(PR_NOTICE, "STB: %s%d measured\n", resource_map[r].name, subid);
>> +
>> + return STB_SUCCESS;
>> +}
>> +
>> +int sb_verify(enum resource_id id, uint32_t subid, void *buf)
>> +{
>> + 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 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) {
>> + prlog(PR_EMERG, "STB: %s failed: resource %d/%d, container null\n",
>> + __func__, id, subid);
>> + sb_enforce();
>> + }
>> +
>> + /* verify failed */
>> + 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);
>
> If we're not enforcing, would we not end up saying "Verified" and then
> returning success? That seems pretty counter-intuitive for a sb_verify()
> function that should probably fail if things fail to verify.
>
> In fact... the abort()/assert()/sound_the_alarm() should probably come
> in the caller to sb_verify() ?
>
We could let the caller enforce secure mode (abort, assert, etc), but if
sb_verify() does that, we make sure that secure mode will be properly
enforced. Currently, if sb_verify() fails, it enforces secure mode by
aborting the boot process, but ideally we think the BMC should apply the
policy (reboot side A, boot side B, shutdown, etc).
The caller will not get back the control if sb_verify() fails. I am not
sure if the caller would like to check the return code just to print
verified. That's why I opted to log something if sb_verify() success.
Printing that the image X has been verified helps to understand what
images were loaded and verified at boot time.
>> diff --git a/libstb/stb.h b/libstb/stb.h
>> new file mode 100644
>> index 0000000..478a061
>> --- /dev/null
>> +++ b/libstb/stb.h
>> @@ -0,0 +1,74 @@
>> +/* 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.
>> + */
>> +
>> +#ifndef __STB_H
>> +#define __STB_H
>> +
>> +/**
>> + * This reads secure mode and trusted mode from device tree and
>> + * loads drivers accordingly.
>> + */
>> +extern void stb_init(void);
>> +
>> +/**
>> + * As defined in the TCG Platform Firmware Profile specification, the
>> + * digest of 0xFFFFFFFF or 0x00000000 must be extended in PCR[0-7] and
>> + * an EV_SEPARATOR event must be recorded in the event log for PCR[0-7]
>> + * prior to the first invocation of the first Ready to Boot call.
>> + *
>> + * This function should be called before the control is passed to petitboot
>> + * kernel in order to do the proper PCR extend and event log recording as
>> + * defined above. This function also deallocates the memory allocated for secure
>> + * and trusted boot.
>> + */
>> +extern int stb_final(void);
>> +
>> +/**
>> + * sb_verify - verify a resource
>> + * @id : resource id
>> + * @subid: subpartition id
>> + * @buf : data to be verified
>> + *
>> + * This verifies the integrity and authenticity of a resource downloaded from
>> + * PNOR if secure mode is on. The verification is done by the
>> + * verification code flashed in the secure ROM.
>> + *
>> + * For more information refer to 'doc/stb.txt'
>> + *
>> + * returns: STB_SUCCESS otherwise the boot process is aborted
>> + */
>> +extern int sb_verify(enum resource_id id, uint32_t subid, void *buf);
>
> size parameter?
>
Yes, makes sense.
len parameter added and checked if it is bigger than
SECURE_BOOT_HEADERS_SIZE just for sanity check since the whole container
header will be verified by the ROM code.
>> +/**
>> + * tb_measure - measure a resource
>> + * @id : resource id
>> + * @subid : subpartition id
>> + * @buf : data to be measured
>> + * @len : buf length
>> + *
>> + * This measures 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
>> + * the sha1 and sha256 measurements are extended in the mapped PCR.
>> + *
>> + * For more information please refer to 'doc/stb.txt'
>> + *
>> + * returns: STB_SUCCESS or an error as defined in status_codes.h
>> + */
>> +extern int tb_measure(enum resource_id id, uint32_t subid, void *buf,
>> + size_t len);
>> +
>> +#endif /* __STB_H */
>
More information about the Skiboot
mailing list