226 lines
6.0 KiB
C
226 lines
6.0 KiB
C
/* 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 <skiboot.h>
|
|
#include <device.h>
|
|
#include <nvram.h>
|
|
#include <opal-api.h>
|
|
#include <inttypes.h>
|
|
#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_dangerous("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;
|
|
}
|