266 lines
6.9 KiB
C
266 lines
6.9 KiB
C
/* Copyright 2013-2017 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 <skiboot.h>
|
|
#include <device.h>
|
|
#include <nvram.h>
|
|
#include <opal-api.h>
|
|
#include "secureboot.h"
|
|
#include "trustedboot.h"
|
|
#include "tpm_chip.h"
|
|
#include "tss/trustedTypes.H"
|
|
|
|
/* For debugging only */
|
|
//#define STB_DEBUG
|
|
|
|
static bool trusted_mode = false;
|
|
static bool trusted_init = false;
|
|
static bool boot_services_exited = false;
|
|
|
|
/*
|
|
* Partitions retrieved from PNOR must be extended to the proper PCR and
|
|
* recorded in the event log. Later, customers may use: the PCR values to attest
|
|
* the boot security, and the event log to inspect what measurements were
|
|
* extended to the PCRs.
|
|
*
|
|
* The whitelist below should map every skiboot event (or resource) to a PCR
|
|
* following the TCG PC Client Platform Firmware Profile specification,
|
|
* Family 2.0, Level 00, Revision 1.03 v51.
|
|
*
|
|
* Convention for skiboot events:
|
|
* - Events that represents data should be extended to PCR 4.
|
|
* - Events that represents config should be extended to PCR 5.
|
|
* - For the lack of an event type that fits the specific purpose,
|
|
* both data and config events should be logged as EV_COMPACT_HASH.
|
|
*/
|
|
static struct {
|
|
enum resource_id id;
|
|
TPM_Pcr pcr;
|
|
} resources[] = {
|
|
{ RESOURCE_ID_IMA_CATALOG, PCR_4},
|
|
{ RESOURCE_ID_KERNEL, PCR_4},
|
|
{ RESOURCE_ID_CAPP, PCR_4},
|
|
{ RESOURCE_ID_VERSION, PCR_4}, /* Also data for Hostboot */
|
|
};
|
|
|
|
/*
|
|
* Event Separator - digest of 0xFFFFFFFF
|
|
*/
|
|
static struct {
|
|
const unsigned char *event;
|
|
const unsigned char *sha1;
|
|
const unsigned char *sha256;
|
|
} ev_separator = {
|
|
|
|
.event = "\xff\xff\xff\xff",
|
|
|
|
.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 TPM_Pcr map_pcr(enum resource_id id)
|
|
{
|
|
int i;
|
|
for (i = 0; i < ARRAY_SIZE(resources); i++) {
|
|
if (resources[i].id == id)
|
|
return resources[i].pcr;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void trustedboot_init(void)
|
|
{
|
|
struct dt_node *node;
|
|
|
|
node = dt_find_by_path(dt_root, "/ibm,secureboot");
|
|
if (!node) {
|
|
prlog(PR_NOTICE, "trusted boot not supported\n");
|
|
return;
|
|
}
|
|
|
|
if (!secureboot_is_compatible(node, NULL, NULL)) {
|
|
/**
|
|
* @fwts-label TrustedBootNotCompatible
|
|
* @fwts-advice Compatible trustedboot driver not found. Probably,
|
|
* hostboot/mambo/skiboot has updated the
|
|
* /ibm,secureboot/compatible without adding a driver that
|
|
* supports it.
|
|
*/
|
|
prlog(PR_ERR, "trustedboot init FAILED, '%s' node not "
|
|
"compatible.\n", node->name);
|
|
return;
|
|
}
|
|
|
|
if (nvram_query_eq_dangerous("force-trusted-mode", "true")) {
|
|
trusted_mode = true;
|
|
prlog(PR_NOTICE, "trusted mode on (FORCED by nvram)\n");
|
|
} else {
|
|
trusted_mode = dt_has_node_property(node, "trusted-enabled", NULL);
|
|
prlog(PR_INFO, "trusted mode %s\n",
|
|
trusted_mode ? "on" : "off");
|
|
}
|
|
|
|
if (!trusted_mode)
|
|
return;
|
|
|
|
cvc_init();
|
|
tpm_init();
|
|
|
|
trusted_init = true;
|
|
}
|
|
|
|
int trustedboot_exit_boot_services(void)
|
|
{
|
|
uint32_t pcr;
|
|
int rc = 0;
|
|
bool failed = false;
|
|
|
|
boot_services_exited = true;
|
|
|
|
if (!trusted_mode)
|
|
goto out_free;
|
|
|
|
#ifdef STB_DEBUG
|
|
prlog(PR_NOTICE, "ev_separator.event: %s\n", ev_separator.event);
|
|
prlog(PR_NOTICE, "ev_separator.sha1:\n");
|
|
stb_print_data((uint8_t*) ev_separator.sha1, TPM_ALG_SHA1_SIZE);
|
|
prlog(PR_NOTICE, "ev_separator.sha256:\n");
|
|
stb_print_data((uint8_t*) ev_separator.sha256, TPM_ALG_SHA256_SIZE);
|
|
#endif
|
|
/*
|
|
* Extend the digest of 0xFFFFFFFF to PCR[0-7] and record it as
|
|
* EV_SEPARATOR
|
|
*/
|
|
for (pcr = 0; pcr < 8; pcr++) {
|
|
rc = tpm_extendl(pcr, TPM_ALG_SHA256,
|
|
(uint8_t*) ev_separator.sha256,
|
|
TPM_ALG_SHA256_SIZE, TPM_ALG_SHA1,
|
|
(uint8_t*) ev_separator.sha1,
|
|
TPM_ALG_SHA1_SIZE, EV_SEPARATOR,
|
|
ev_separator.event);
|
|
if (rc)
|
|
failed = true;
|
|
}
|
|
tpm_add_status_property();
|
|
|
|
out_free:
|
|
tpm_cleanup();
|
|
|
|
return (failed) ? -1 : 0;
|
|
}
|
|
|
|
int trustedboot_measure(enum resource_id id, void *buf, size_t len)
|
|
{
|
|
uint8_t digest[SHA512_DIGEST_LENGTH];
|
|
void *buf_aux;
|
|
size_t len_aux;
|
|
const char *name;
|
|
TPM_Pcr pcr;
|
|
int rc = -1;
|
|
|
|
if (!trusted_mode)
|
|
return 1;
|
|
|
|
name = flash_map_resource_name(id);
|
|
if (!name) {
|
|
/**
|
|
* @fwts-label ResourceNotMeasuredUnknown
|
|
* @fwts-advice This is a bug in the trustedboot_measure()
|
|
* caller, which is passing an unknown resource_id.
|
|
*/
|
|
prlog(PR_ERR, "resource NOT MEASURED, resource_id=%d unknown\n", id);
|
|
return -1;
|
|
}
|
|
|
|
if (!trusted_init) {
|
|
prlog(PR_ERR, "resource NOT MEASURED, resource_id=%d "
|
|
"trustedboot not yet initialized\n", id);
|
|
return -1;
|
|
}
|
|
|
|
if (boot_services_exited) {
|
|
prlog(PR_ERR, "%s NOT MEASURED. Already exited from boot "
|
|
"services\n", name);
|
|
return -1;
|
|
}
|
|
pcr = map_pcr(id);
|
|
if (pcr == -1) {
|
|
/**
|
|
* @fwts-label ResourceNotMappedToPCR
|
|
* @fwts-advice This is a bug. The resource cannot be measured
|
|
* because it is not mapped to a PCR in the resources[] array.
|
|
*/
|
|
prlog(PR_ERR, "%s NOT MEASURED, it's not mapped to a PCR\n", name);
|
|
return -1;
|
|
}
|
|
if (!buf) {
|
|
/**
|
|
* @fwts-label ResourceNotMeasuredNull
|
|
* @fwts-advice This is a bug. The trustedboot_measure() caller
|
|
* provided a NULL container.
|
|
*/
|
|
prlog(PR_ERR, "%s NOT MEASURED, it's null\n", name);
|
|
return -1;
|
|
}
|
|
if (stb_is_container(buf, len)) {
|
|
buf_aux = buf + SECURE_BOOT_HEADERS_SIZE;
|
|
len_aux = len - SECURE_BOOT_HEADERS_SIZE;
|
|
} else {
|
|
buf_aux = buf;
|
|
len_aux = len;
|
|
}
|
|
|
|
rc = call_cvc_sha512(buf_aux, len_aux, digest, SHA512_DIGEST_LENGTH);
|
|
|
|
if (rc == OPAL_SUCCESS) {
|
|
prlog(PR_NOTICE, "%s hash calculated\n", name);
|
|
} else if (rc == OPAL_PARAMETER) {
|
|
prlog(PR_ERR, "%s NOT MEASURED, invalid param. buf=%p, "
|
|
"len=%zd, digest=%p\n", name, buf_aux,
|
|
len_aux, digest);
|
|
return -1;
|
|
} else if (rc == OPAL_UNSUPPORTED) {
|
|
prlog(PR_ERR, "%s NOT MEASURED, CVC-sha512 service not "
|
|
"supported\n", name);
|
|
return -1;
|
|
} else {
|
|
prlog(PR_ERR, "%s NOT MEASURED, unknown CVC-sha512 error. "
|
|
"rc=%d\n", name, rc);
|
|
return -1;
|
|
}
|
|
|
|
#ifdef STB_DEBUG
|
|
stb_print_data(digest, TPM_ALG_SHA256_SIZE);
|
|
#endif
|
|
/*
|
|
* Extend the given PCR number in both sha256 and sha1 banks with the
|
|
* sha512 hash calculated. The hash is truncated accordingly to fit the
|
|
* PCR.
|
|
*/
|
|
return tpm_extendl(pcr,
|
|
TPM_ALG_SHA256, digest, TPM_ALG_SHA256_SIZE,
|
|
TPM_ALG_SHA1, digest, TPM_ALG_SHA1_SIZE,
|
|
EV_COMPACT_HASH, name);
|
|
}
|