| /* 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" |
| |
| /* For debugging only */ |
| //#define STB_DEBUG |
| |
| static bool trusted_mode = false; |
| static bool trusted_init = false; |
| static bool boot_services_exited = false; |
| |
| /* |
| * 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 { |
| enum resource_id id; |
| TPM_Pcr pcr; |
| } resources[] = { |
| { RESOURCE_ID_IMA_CATALOG, PCR_2 }, |
| { RESOURCE_ID_KERNEL, PCR_4 }, |
| { RESOURCE_ID_CAPP, PCR_2 }, |
| { RESOURCE_ID_VERSION, PCR_3 }, |
| }; |
| |
| /* |
| * 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("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_NOTICE, "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 |
| /* |
| * As defined in the TCG Platform Firmware PWe 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*) 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_ACTION, name); |
| } |