1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
|
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/* Copyright 2020 IBM Corp. */
#ifndef pr_fmt
#define pr_fmt(fmt) "SECBOOT_TPM: " fmt
#endif
#include <stdlib.h>
#include <skiboot.h>
#include <opal.h>
#include <mbedtls/sha256.h>
#include "../secvar.h"
#include "../secvar_devtree.h"
#include "secboot_tpm.h"
#include <tssskiboot.h>
#include <ibmtss/TPM_Types.h>
#define CYCLE_BIT(b) (b^0x1)
#define SECBOOT_TPM_MAX_VAR_SIZE 8192
struct secboot *secboot_image = NULL;
struct tpmnv_vars *tpmnv_vars_image = NULL;
struct tpmnv_control *tpmnv_control_image = NULL;
const size_t tpmnv_vars_size = 2048;
/* Expected TPM NV index name field from NV_ReadPublic given our known
* set of attributes (see tss_nv_define_space).
* See Part 1 Section 16, and Part 2 Section 13.5 of the TPM Specification
* for how this is calculated
*
* These hashes are calculated and checked BEFORE TPM2_NV_WriteLock is called,
* which alters the hash slightly as it sets TPMA_NV_WRITELOCKED
*/
const uint8_t tpmnv_vars_name[] = {
0x00, 0x0b, 0x7a, 0xdb, 0x70, 0xdd, 0x27, 0x94, 0x93, 0x26, 0x11, 0xe2, 0x97,
0x00, 0x77, 0x22, 0x4d, 0x5a, 0x74, 0xf8, 0x91, 0x6f, 0xbf, 0xf8, 0x51, 0x4a,
0x67, 0x6f, 0xd9, 0xa8, 0xc3, 0xfc, 0x39, 0xed,
};
const uint8_t tpmnv_control_name[] = {
0x00, 0x0b, 0xad, 0x47, 0x6b, 0xa5, 0xdf, 0xb1, 0xe2, 0x18, 0x50, 0xf6, 0x05,
0x67, 0xe8, 0x8b, 0xa9, 0x0f, 0x86, 0x1f, 0x06, 0xab, 0x43, 0x96, 0x7f, 0x6e,
0x85, 0x33, 0x5b, 0xa6, 0xf0, 0x63, 0x73, 0xd0,
};
const uint8_t tpmnv_vars_prov_name[] = {
0x00, 0x0b, 0x58, 0x36, 0x2c, 0xbf, 0xec, 0x0e, 0xcc, 0xbf, 0xa9, 0x41, 0x94,
0xe9, 0x95, 0xe8, 0x3b, 0xd7, 0x8b, 0x52, 0xac, 0x61, 0x6f, 0xe6, 0x42, 0x93,
0xbb, 0x5a, 0x79, 0x9f, 0xcc, 0x60, 0x5e, 0x8d,
};
const uint8_t tpmnv_control_prov_name[] = {
0x00, 0x0b, 0x7b, 0xd6, 0x02, 0xac, 0xf5, 0x34, 0x54, 0x5c, 0x3e, 0xda, 0xe5,
0xb2, 0xe4, 0x93, 0x4f, 0x36, 0xfb, 0x7f, 0xea, 0xbe, 0xfa, 0x3c, 0xfe, 0xed,
0x6a, 0x12, 0xfb, 0xc8, 0xf7, 0x92, 0x0e, 0xd3,
};
/* Calculate a SHA256 hash over the supplied buffer */
static int calc_bank_hash(char *target_hash, const char *source_buf, uint64_t size)
{
mbedtls_sha256_context ctx;
int rc;
mbedtls_sha256_init(&ctx);
rc = mbedtls_sha256_update_ret(&ctx, source_buf, size);
if (rc)
goto out;
mbedtls_sha256_finish_ret(&ctx, target_hash);
if (rc)
goto out;
out:
mbedtls_sha256_free(&ctx);
return rc;
}
/* Reformat the TPMNV space */
static int tpmnv_format(void)
{
int rc;
memset(tpmnv_vars_image, 0x00, tpmnv_vars_size);
memset(tpmnv_control_image, 0x00, sizeof(struct tpmnv_control));
tpmnv_vars_image->header.magic_number = SECBOOT_MAGIC_NUMBER;
tpmnv_vars_image->header.version = SECBOOT_VERSION;
tpmnv_control_image->header.magic_number = SECBOOT_MAGIC_NUMBER;
tpmnv_control_image->header.version = SECBOOT_VERSION;
/* Counts as first write to the TPM NV, which sets the
* TPMA_NVA_WRITTEN attribute */
rc = tpmnv_ops.write(SECBOOT_TPMNV_VARS_INDEX,
tpmnv_vars_image,
tpmnv_vars_size, 0);
if (rc) {
prlog(PR_ERR, "Could not write new formatted data to VARS index, rc=%d\n", rc);
return rc;
}
rc = tpmnv_ops.write(SECBOOT_TPMNV_CONTROL_INDEX,
tpmnv_control_image,
sizeof(struct tpmnv_control), 0);
if (rc)
prlog(PR_ERR, "Could not write new formatted data to CONTROL index, rc=%d\n", rc);
return rc;
}
/* Reformat the secboot PNOR space */
static int secboot_format(void)
{
int rc;
memset(secboot_image, 0x00, sizeof(struct secboot));
secboot_image->header.magic_number = SECBOOT_MAGIC_NUMBER;
secboot_image->header.version = SECBOOT_VERSION;
/* Write the hash of the empty bank to the tpm so future loads work */
rc = calc_bank_hash(tpmnv_control_image->bank_hash[0],
secboot_image->bank[0],
SECBOOT_VARIABLE_BANK_SIZE);
if (rc) {
prlog(PR_ERR, "Bank hash failed to calculate somehow\n");
return rc;
}
rc = tpmnv_ops.write(SECBOOT_TPMNV_CONTROL_INDEX,
tpmnv_control_image->bank_hash[0],
SHA256_DIGEST_SIZE,
offsetof(struct tpmnv_control,
bank_hash[0]));
if (rc) {
prlog(PR_ERR, "Could not write fresh formatted bank hashes to CONTROL index, rc=%d\n", rc);
return rc;
}
rc = flash_secboot_write(0, secboot_image, sizeof(struct secboot));
if (rc)
prlog(PR_ERR, "Could not write formatted data to PNOR, rc=%d\n", rc);
return rc;
}
/*
* Serialize one variable to a target memory location.
* Returns the advanced target pointer,
* NULL if advanced pointer would exceed the supplied bound
*/
static char *secboot_serialize_secvar(char *target, const struct secvar *var, const char *end)
{
if ((target + sizeof(uint64_t) + sizeof(uint64_t)
+ var->key_len + var->data_size) > end)
return NULL;
*((uint64_t*) target) = cpu_to_be64(var->key_len);
target += sizeof(var->key_len);
*((uint64_t*) target) = cpu_to_be64(var->data_size);
target += sizeof(var->data_size);
memcpy(target, var->key, var->key_len);
target += var->key_len;
memcpy(target, var->data, var->data_size);
target += var->data_size;
return target;
}
/* Flattens a linked-list bank into a contiguous buffer for writing */
static int secboot_serialize_bank(const struct list_head *bank, char *target,
size_t target_size, int flags)
{
struct secvar *var;
char *end = target + target_size;
assert(bank);
assert(target);
memset(target, 0x00, target_size);
list_for_each(bank, var, link) {
if (var->flags != flags)
continue;
target = secboot_serialize_secvar(target, var, end);
if (!target) {
prlog(PR_ERR, "Ran out of %s space, giving up!",
(flags & SECVAR_FLAG_PROTECTED) ? "TPMNV" : "PNOR");
return OPAL_EMPTY;
}
}
return OPAL_SUCCESS;
}
/* Helper for the variable-bank specific writing logic */
static int secboot_tpm_write_variable_bank(const struct list_head *bank)
{
int rc;
uint64_t bit;
bit = CYCLE_BIT(tpmnv_control_image->active_bit);
/* Serialize TPMNV variables */
rc = secboot_serialize_bank(bank, tpmnv_vars_image->vars, tpmnv_vars_size - sizeof(struct tpmnv_vars), SECVAR_FLAG_PROTECTED);
if (rc)
goto out;
/* Write TPMNV variables to actual NV */
rc = tpmnv_ops.write(SECBOOT_TPMNV_VARS_INDEX, tpmnv_vars_image, tpmnv_vars_size, 0);
if (rc)
goto out;
/* Serialize the PNOR variables, but don't write to flash until after the bank hash */
rc = secboot_serialize_bank(bank, secboot_image->bank[bit], SECBOOT_VARIABLE_BANK_SIZE, 0);
if (rc)
goto out;
/* Calculate the bank hash, and write to TPM NV */
rc = calc_bank_hash(tpmnv_control_image->bank_hash[bit], secboot_image->bank[bit], SECBOOT_VARIABLE_BANK_SIZE);
if (rc)
goto out;
rc = tpmnv_ops.write(SECBOOT_TPMNV_CONTROL_INDEX, tpmnv_control_image->bank_hash[bit],
SHA256_DIGEST_LENGTH, offsetof(struct tpmnv_control, bank_hash[bit]));
if (rc)
goto out;
/* Write new variable bank to pnor */
rc = flash_secboot_write(0, secboot_image, sizeof(struct secboot));
if (rc)
goto out;
/* Flip the bit, and write to TPM NV */
tpmnv_control_image->active_bit = bit;
rc = tpmnv_ops.write(SECBOOT_TPMNV_CONTROL_INDEX,
&tpmnv_control_image->active_bit,
sizeof(tpmnv_control_image->active_bit),
offsetof(struct tpmnv_control, active_bit));
out:
return rc;
}
static int secboot_tpm_write_bank(struct list_head *bank, int section)
{
int rc;
switch (section) {
case SECVAR_VARIABLE_BANK:
rc = secboot_tpm_write_variable_bank(bank);
break;
case SECVAR_UPDATE_BANK:
memset(secboot_image->update, 0, SECBOOT_UPDATE_BANK_SIZE);
rc = secboot_serialize_bank(bank, secboot_image->update,
SECBOOT_UPDATE_BANK_SIZE, 0);
if (rc)
break;
rc = flash_secboot_write(0, secboot_image,
sizeof(struct secboot));
break;
default:
rc = OPAL_HARDWARE;
}
return rc;
}
/*
* Deserialize a single secvar from a buffer.
* Returns an advanced pointer, and an allocated secvar in *var.
* Returns NULL if out of bounds reached, or out of memory.
*/
static int secboot_deserialize_secvar(struct secvar **var, char **src, const char *end)
{
uint64_t key_len;
uint64_t data_size;
struct secvar *ret;
assert(var);
/* Load in the two header values */
key_len = be64_to_cpu(*((uint64_t *) *src));
*src += sizeof(uint64_t);
data_size = be64_to_cpu(*((uint64_t *) *src));
*src += sizeof(uint64_t);
/* Check if we've reached the last var to deserialize */
if ((key_len == 0) && (data_size == 0)) {
return OPAL_EMPTY;
}
if (key_len > SECVAR_MAX_KEY_LEN) {
prlog(PR_ERR, "Deserialization failed: key length exceeded maximum value"
"%llu > %u", key_len, SECVAR_MAX_KEY_LEN);
return OPAL_RESOURCE;
}
if (data_size > SECBOOT_TPM_MAX_VAR_SIZE) {
prlog(PR_ERR, "Deserialization failed: data size exceeded maximum value"
"%llu > %u", key_len, SECBOOT_TPM_MAX_VAR_SIZE);
return OPAL_RESOURCE;
}
/* Make sure these fields aren't oversized... */
if ((*src + key_len + data_size) > end) {
*var = NULL;
prlog(PR_ERR, "key_len or data_size exceeded the expected bounds");
return OPAL_RESOURCE;
}
ret = alloc_secvar(key_len, data_size);
if (!ret) {
*var = NULL;
prlog(PR_ERR, "Out of memory, could not allocate new secvar");
return OPAL_NO_MEM;
}
/* Load in variable-sized data */
memcpy(ret->key, *src, ret->key_len);
*src += ret->key_len;
memcpy(ret->data, *src, ret->data_size);
*src += ret->data_size;
*var = ret;
return OPAL_SUCCESS;
}
/* Load variables from a flattened buffer into a bank list */
static int secboot_tpm_deserialize_from_buffer(struct list_head *bank, char *src,
uint64_t size, uint64_t flags)
{
struct secvar *var;
char *cur;
char *end;
int rc = 0;
cur = src;
end = src + size;
while (cur < end) {
/* Ensure there is enough space to even check for another var header */
if ((end - cur) < (sizeof(uint64_t) * 2))
break;
rc = secboot_deserialize_secvar(&var, &cur, end);
switch (rc) {
case OPAL_RESOURCE:
case OPAL_NO_MEM:
goto fail;
case OPAL_EMPTY:
goto done;
default: assert(1);
}
var->flags |= flags;
list_add_tail(bank, &var->link);
}
done:
return OPAL_SUCCESS;
fail:
clear_bank_list(bank);
return rc;
}
static int secboot_tpm_load_variable_bank(struct list_head *bank)
{
char bank_hash[SHA256_DIGEST_LENGTH];
uint64_t bit = tpmnv_control_image->active_bit;
int rc;
/* Check the hash of the bank we loaded from PNOR
* versus the expected hash in TPM NV */
rc = calc_bank_hash(bank_hash,
secboot_image->bank[bit],
SECBOOT_VARIABLE_BANK_SIZE);
if (rc)
return rc;
if (memcmp(bank_hash,
tpmnv_control_image->bank_hash[bit],
SHA256_DIGEST_LENGTH))
/* Tampered pnor space detected, abandon ship */
return OPAL_PERMISSION;
rc = secboot_tpm_deserialize_from_buffer(bank, tpmnv_vars_image->vars, tpmnv_vars_size, SECVAR_FLAG_PROTECTED);
if (rc)
return rc;
return secboot_tpm_deserialize_from_buffer(bank, secboot_image->bank[bit], SECBOOT_VARIABLE_BANK_SIZE, 0);
}
static int secboot_tpm_load_bank(struct list_head *bank, int section)
{
switch (section) {
case SECVAR_VARIABLE_BANK:
return secboot_tpm_load_variable_bank(bank);
case SECVAR_UPDATE_BANK:
return secboot_tpm_deserialize_from_buffer(bank, secboot_image->update, SECBOOT_UPDATE_BANK_SIZE, 0);
}
return OPAL_HARDWARE;
}
static int secboot_tpm_get_tpmnv_names(char *nv_vars_name, char *nv_control_name)
{
TPMS_NV_PUBLIC nv_public; /* Throwaway, we only want the name field */
TPM2B_NAME vars_tmp;
TPM2B_NAME control_tmp;
int rc;
rc = tpmnv_ops.readpublic(SECBOOT_TPMNV_VARS_INDEX,
&nv_public,
&vars_tmp);
if (rc) {
prlog(PR_ERR, "Failed to readpublic from the VARS index, rc=%d\n", rc);
return rc;
}
rc = tpmnv_ops.readpublic(SECBOOT_TPMNV_CONTROL_INDEX,
&nv_public,
&control_tmp);
if (rc) {
prlog(PR_ERR, "Failed to readpublic from the CONTROL index, rc=%d\n", rc);
return rc;
}
memcpy(nv_vars_name, vars_tmp.t.name, MIN(sizeof(tpmnv_vars_name), vars_tmp.t.size));
memcpy(nv_control_name, control_tmp.t.name, MIN(sizeof(tpmnv_control_name), control_tmp.t.size));
return OPAL_SUCCESS;
}
/* Ensure the NV indices were defined with the correct set of attributes */
static int secboot_tpm_check_tpmnv_attrs(char *nv_vars_name, char *nv_control_name)
{
if (memcmp(tpmnv_vars_name,
nv_vars_name,
sizeof(tpmnv_vars_name))) {
prlog(PR_ERR, "VARS index not defined with the correct attributes\n");
return OPAL_RESOURCE;
}
if (memcmp(tpmnv_control_name,
nv_control_name,
sizeof(tpmnv_control_name))) {
prlog(PR_ERR, "CONTROL index not defined with the correct attributes\n");
return OPAL_RESOURCE;
}
return OPAL_SUCCESS;
}
static bool secboot_tpm_check_provisioned_indices(char *nv_vars_name, char *nv_control_name)
{
/* Check for provisioned NV indices, redefine them if detected. */
if (!memcmp(tpmnv_vars_prov_name,
nv_vars_name,
sizeof(tpmnv_vars_prov_name)) &&
!memcmp(tpmnv_control_prov_name,
nv_control_name,
sizeof(tpmnv_control_prov_name))) {
return true;
}
/*
* If one matches but the other doesn't, do NOT redefine.
* The next step should detect they don't match the expected values
* and fail the boot.
*/
return false;
}
static int secboot_tpm_define_indices(void)
{
int rc = OPAL_SUCCESS;
rc = tpmnv_ops.definespace(SECBOOT_TPMNV_VARS_INDEX, tpmnv_vars_size);
if (rc) {
prlog(PR_ERR, "Failed to define the VARS index, rc=%d\n", rc);
return rc;
}
rc = tpmnv_ops.definespace(SECBOOT_TPMNV_CONTROL_INDEX, sizeof(struct tpmnv_control));
if (rc) {
prlog(PR_ERR, "Failed to define the CONTROL index, rc=%d\n", rc);
return rc;
}
rc = tpmnv_format();
if (rc)
return rc;
/* TPM NV just got redefined, so unconditionally format the SECBOOT partition */
return secboot_format();
}
static int secboot_tpm_undefine_indices(bool *vars_defined, bool *control_defined)
{
int rc;
if (vars_defined) {
rc = tpmnv_ops.undefinespace(SECBOOT_TPMNV_VARS_INDEX);
if (rc) {
prlog(PR_ERR, "Failed to undefine VARS, something is seriously wrong\n");
return rc;
}
}
if (control_defined) {
rc = tpmnv_ops.undefinespace(SECBOOT_TPMNV_CONTROL_INDEX);
if (rc) {
prlog(PR_ERR, "Failed to undefine CONTROL, something is seriously wrong\n");
return rc;
}
}
*vars_defined = *control_defined = false;
return OPAL_SUCCESS;
}
static int secboot_tpm_store_init(void)
{
int rc;
unsigned int secboot_size;
TPMI_RH_NV_INDEX *indices = NULL;
char nv_vars_name[sizeof(tpmnv_vars_name)];
char nv_control_name[sizeof(tpmnv_control_name)];
size_t count = 0;
bool control_defined = false;
bool vars_defined = false;
int i;
if (secboot_image)
return OPAL_SUCCESS;
prlog(PR_DEBUG, "Initializing for pnor+tpm based platform\n");
/* Initialize SECBOOT first, we may need to format this later */
rc = flash_secboot_info(&secboot_size);
if (rc) {
prlog(PR_ERR, "error %d retrieving keystore info\n", rc);
goto error;
}
if (sizeof(struct secboot) > secboot_size) {
prlog(PR_ERR, "secboot partition %d KB too small. min=%ld\n",
secboot_size >> 10, sizeof(struct secboot));
rc = OPAL_RESOURCE;
goto error;
}
secboot_image = memalign(0x1000, sizeof(struct secboot));
if (!secboot_image) {
prlog(PR_ERR, "Failed to allocate space for the secboot image\n");
rc = OPAL_NO_MEM;
goto error;
}
/* Read in the PNOR data, bank hash is checked on call to .load_bank() */
rc = flash_secboot_read(secboot_image, 0, sizeof(struct secboot));
if (rc) {
prlog(PR_ERR, "failed to read the secboot partition, rc=%d\n", rc);
goto error;
}
/* Allocate the tpmnv data buffers */
tpmnv_vars_image = zalloc(tpmnv_vars_size);
if (!tpmnv_vars_image)
return OPAL_NO_MEM;
tpmnv_control_image = zalloc(sizeof(struct tpmnv_control));
if (!tpmnv_control_image)
return OPAL_NO_MEM;
/* Check if the NV indices have been defined already */
rc = tpmnv_ops.getindices(&indices, &count);
if (rc) {
prlog(PR_ERR, "Could not load defined indicies from TPM, rc=%d\n", rc);
goto error;
}
for (i = 0; i < count; i++) {
if (indices[i] == SECBOOT_TPMNV_VARS_INDEX)
vars_defined = true;
else if (indices[i] == SECBOOT_TPMNV_CONTROL_INDEX)
control_defined = true;
}
free(indices);
/* Undefine the NV indices if physical presence has been asserted */
if (secvar_check_physical_presence()) {
prlog(PR_INFO, "Physical presence asserted, redefining NV indices, and resetting keystore\n");
rc = secboot_tpm_undefine_indices(&vars_defined, &control_defined);
if (rc)
goto error;
rc = secboot_tpm_define_indices();
if (rc)
goto error;
/* Indices got defined and formatted, we're done here */
goto done;
}
/* Determine if we need to define the indices. These should BOTH be false or true */
if (!vars_defined && !control_defined) {
rc = secboot_tpm_define_indices();
if (rc)
goto error;
/* Indices got defined and formatted, we're done here */
goto done;
}
if (vars_defined ^ control_defined) {
/* This should never happen. Both indices should be defined at the same
* time. Otherwise something seriously went wrong. */
prlog(PR_ERR, "NV indices defined with unexpected attributes. Assert physical presence to clear\n");
goto error;
}
/* Both indices are defined, now need to validate their contents */
rc = secboot_tpm_get_tpmnv_names(nv_vars_name, nv_control_name);
if (rc)
goto error;
/* Check for provisioned TPMNV indices, redefine them if detected */
if (secboot_tpm_check_provisioned_indices(nv_vars_name, nv_control_name)) {
prlog(PR_INFO, "Provisioned TPM NV indices detected, redefining NV indices, and resetting keystore\n");
rc = secboot_tpm_undefine_indices(&vars_defined, &control_defined);
if (rc)
goto error;
rc = secboot_tpm_define_indices();
if (rc)
goto error;
/* Indices got defined and formatted, we're done here */
goto done;
}
/* Otherwise, ensure the NV indices were defined with the correct set of attributes */
rc = secboot_tpm_check_tpmnv_attrs(nv_vars_name, nv_control_name);
if (rc)
goto error;
/* TPMNV indices exist, are correct, and weren't just formatted, so read them in */
rc = tpmnv_ops.read(SECBOOT_TPMNV_VARS_INDEX,
tpmnv_vars_image,
tpmnv_vars_size, 0);
if (rc) {
prlog(PR_ERR, "Failed to read from the VARS index\n");
goto error;
}
rc = tpmnv_ops.read(SECBOOT_TPMNV_CONTROL_INDEX,
tpmnv_control_image,
sizeof(struct tpmnv_control), 0);
if (rc) {
prlog(PR_ERR, "Failed to read from the CONTROL index\n");
goto error;
}
/* Verify the header information is correct */
if (tpmnv_vars_image->header.magic_number != SECBOOT_MAGIC_NUMBER ||
tpmnv_control_image->header.magic_number != SECBOOT_MAGIC_NUMBER ||
tpmnv_vars_image->header.version != SECBOOT_VERSION ||
tpmnv_control_image->header.version != SECBOOT_VERSION) {
prlog(PR_ERR, "TPMNV indices defined, but contain bad data. Assert physical presence to clear\n");
goto error;
}
/* Verify the secboot partition header information,
* reformat if incorrect
* Note: Future variants should attempt to handle older versions safely
*/
if (secboot_image->header.magic_number != SECBOOT_MAGIC_NUMBER ||
secboot_image->header.version != SECBOOT_VERSION) {
rc = secboot_format();
if (rc)
goto error;
}
done:
return OPAL_SUCCESS;
error:
free(secboot_image);
secboot_image = NULL;
free(tpmnv_vars_image);
tpmnv_vars_image = NULL;
free(tpmnv_control_image);
tpmnv_control_image = NULL;
return rc;
}
static void secboot_tpm_lockdown(void)
{
/* Note: While write lock is called here on the two NV indices,
* both indices are also defined on the platform hierarchy.
* The platform hierarchy auth is set later in the skiboot
* initialization process, and not by any secvar-related code.
*/
int rc;
rc = tpmnv_ops.writelock(SECBOOT_TPMNV_VARS_INDEX);
if (rc) {
prlog(PR_EMERG, "TSS Write Lock failed on VARS index, halting.\n");
abort();
}
rc = tpmnv_ops.writelock(SECBOOT_TPMNV_CONTROL_INDEX);
if (rc) {
prlog(PR_EMERG, "TSS Write Lock failed on CONTROL index, halting.\n");
abort();
}
}
struct secvar_storage_driver secboot_tpm_driver = {
.load_bank = secboot_tpm_load_bank,
.write_bank = secboot_tpm_write_bank,
.store_init = secboot_tpm_store_init,
.lockdown = secboot_tpm_lockdown,
.max_var_size = SECBOOT_TPM_MAX_VAR_SIZE,
};
|