blob: 28d004d96e653171f63230a9e81926eb83c208d1 [file] [log] [blame]
/* 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 <nvram.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)
{
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);
if (nvram_query_eq("force-secure-mode", "always")) {
prlog(PR_NOTICE, "STB: secure mode on (FORCED by nvram)\n");
secure_mode = true;
} else if (secure_mode) {
prlog(PR_NOTICE, "STB: secure mode on.\n");
} else {
prlog(PR_NOTICE, "STB: secure mode off\n");
}
#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);
if (nvram_query_eq("force-trusted-mode", "true")) {
prlog(PR_NOTICE, "STB: trusted mode ON (from NVRAM)\n");
trusted_mode = true;
}
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;
bool failed;
rc = 0;
failed = false;
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)
failed = true;
}
tpm_add_status_property();
}
if (rom_driver) {
rom_driver->cleanup();
rom_driver = NULL;
}
tpm_cleanup();
secure_mode = false;
trusted_mode = false;
return (failed) ? STB_MEASURE_FAILED : 0;
}
int tb_measure(enum resource_id id, void *buf, size_t len)
{
int r;
uint8_t digest[SHA512_DIGEST_LENGTH];
const uint8_t *digestp;
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, buf null\n",
__func__, resource_map[r].name);
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 (stb_is_container(buf, len)) {
digestp = stb_sw_payload_hash(buf, len);
if(!digestp) {
prlog(PR_EMERG, "STB Container is corrupt, can't find hash\n");
abort();
}
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);
if (memcmp(digestp, digest, TPM_ALG_SHA256_SIZE) != 0) {
prlog(PR_ALERT, "STB: HASH IN CONTAINER DOESN'T MATCH CONTENT!\n");
prlog(PR_ALERT, "STB: Container hash:\n");
stb_print_data(digestp, TPM_ALG_SHA256_SIZE);
prlog(PR_ALERT, "STB: Computed hash (on %lx bytes):\n", len);
stb_print_data(digest, TPM_ALG_SHA256_SIZE);
if (secure_mode)
abort();
}
} 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.
*/
return 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);
}
int sb_verify(enum resource_id id, 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\n", id);
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, buf %p, len %zd\n",
__func__, id, buf, len);
sb_enforce();
}
if (rom_driver->verify(buf)) {
prlog(PR_EMERG, "STB: %s failed: resource %s, "
"eyecatcher 0x%016llx\n", __func__, name,
*((uint64_t*)buf));
sb_enforce();
}
prlog(PR_NOTICE, "STB: %s verified\n", name);
return 0;
}