| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /******************************************************************************* |
| * Copyright 2017-2018, Fraunhofer SIT sponsored by Infineon Technologies AG |
| * All rights reserved. |
| ******************************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <openssl/sha.h> |
| #include <ctype.h> |
| #include <stdbool.h> |
| #include <openssl/evp.h> |
| #include <json-c/json.h> |
| #include <json-c/json_util.h> |
| #include "tss2_common.h" |
| #include "tss2_tpm2_types.h" |
| #include "fapi_int.h" |
| #include "ifapi_json_deserialize.h" |
| #include "tpm_json_deserialize.h" |
| #include "ifapi_ima_eventlog.h" |
| #include "ifapi_helpers.h" |
| |
| #define LOGMODULE fapijson |
| #include "util/log.h" |
| #include "util/aux_util.h" |
| |
| /* Defines from kernel ima.h */ |
| #define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16 |
| #define IMA_TEMPLATE_NUM_FIELDS_MAX 15 |
| /* Define from kernel crypt.h */ |
| #define CRYPTO_MAX_ALG_NAME 128 |
| |
| static uint32_t endian_swap_32(uint32_t data) { |
| uint32_t converted; |
| uint8_t *bytes = (uint8_t *)&data; |
| uint8_t *tmp = (uint8_t *)&converted; |
| size_t i; |
| for(i=0; i < sizeof(uint32_t); i ++) { |
| tmp[i] = bytes[sizeof(uint32_t) - i - 1]; |
| } |
| return converted; |
| } |
| |
| static bool big_endian_arch(void) { |
| |
| uint32_t test_word; |
| uint8_t *test_byte; |
| |
| test_word = 0xFF000000; |
| test_byte = (uint8_t *) (&test_word); |
| return test_byte[0] == 0xFF; |
| } |
| |
| static bool little_endian_pcr(uint32_t pcr) { |
| |
| uint8_t *test_pcr; |
| |
| test_pcr = (uint8_t *) (&pcr); |
| return test_pcr[0]; |
| } |
| |
| static bool need_to_convert_to_big_endian(IFAPI_IMA_TEMPLATE *template) |
| { |
| return big_endian_arch() && little_endian_pcr(template->header.pcr); |
| } |
| |
| static TSS2_RC |
| get_json_content(json_object *jso, json_object **jso_sub) { |
| if (!ifapi_get_sub_object(jso, CONTENT, jso_sub)) { |
| *jso_sub = json_object_new_object(); |
| return_if_null(*jso_sub, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| json_object_object_add(jso, CONTENT, *jso_sub); |
| } |
| return TSS2_RC_SUCCESS; |
| } |
| |
| static TSS2_RC |
| add_uint8_ary_to_json(UINT8 *buffer, UINT32 size, json_object *jso, const char *jso_tag) |
| { |
| json_object *jso_byte_string = NULL; |
| |
| return_if_null(buffer, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| return_if_null(jso, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| |
| char *hex_string = malloc((size) * 2 + 1); |
| return_if_null(hex_string, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| |
| if (size > 0) { |
| for (size_t i = 0, off = 0; i < size; i++, off += 2) |
| sprintf(&hex_string[off], "%02x", buffer[i]); |
| } |
| hex_string[(size) * 2] = '\0'; |
| jso_byte_string = json_object_new_string(hex_string); |
| SAFE_FREE(hex_string) |
| return_if_null(jso_byte_string, "Out of memory", TSS2_FAPI_RC_MEMORY); |
| |
| json_object_object_add(jso, jso_tag, jso_byte_string); |
| return TSS2_RC_SUCCESS; |
| } |
| |
| static TSS2_RC |
| add_string_to_json(const char *string, json_object *jso, const char *jso_tag) |
| { |
| json_object *jso_string = NULL; |
| |
| return_if_null(string, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| return_if_null(jso, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| |
| jso_string = json_object_new_string(string); |
| return_if_null(jso_string, "Out of memory", TSS2_FAPI_RC_MEMORY); |
| |
| json_object_object_add(jso, jso_tag, jso_string); |
| return TSS2_RC_SUCCESS; |
| } |
| |
| static TSS2_RC |
| add_number_to_json(UINT32 number, json_object *jso, const char *jso_tag) |
| { |
| json_object *jso_number = NULL; |
| |
| return_if_null(jso, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| |
| jso_number = json_object_new_int64(number); |
| return_if_null(jso_number, "Out of memory", TSS2_FAPI_RC_MEMORY); |
| |
| json_object_object_add(jso, jso_tag, jso_number); |
| return TSS2_RC_SUCCESS; |
| } |
| |
| static bool |
| zero_digest(UINT8 *buffer, size_t size) |
| { |
| if (buffer[0] == 0 && memcmp(&buffer[0], &buffer[1], size - 1) == 0) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static UINT8 digest_ff[TPM2_SHA512_DIGEST_SIZE] = { |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| }; |
| |
| /* Replace current sha1 digest with digest of 0xff values. */ |
| static TSS2_RC |
| set_ff_digest(json_object *jso) { |
| TSS2_RC r; |
| json_object |
| *jso_digest = NULL, |
| *jso_ary = NULL, |
| *jso_digest_type = NULL; |
| |
| jso_digest = json_object_new_object(); |
| return_if_null(jso_digest, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| |
| r = add_uint8_ary_to_json(digest_ff,TPM2_SHA1_DIGEST_SIZE, jso_digest, "digest"); |
| return_if_error(r, "Add digest to json"); |
| |
| jso_digest_type = NULL; |
| jso_digest_type = json_object_new_string ("sha1"); |
| goto_if_null(jso_digest_type, "Out of memory.", TSS2_FAPI_RC_MEMORY, error); |
| |
| json_object_object_add(jso_digest, "hashAlg", jso_digest_type); |
| |
| jso_ary = json_object_new_array(); |
| goto_if_null(jso_ary, "Out of memory.", TSS2_FAPI_RC_MEMORY, error); |
| |
| json_object_array_add(jso_ary, jso_digest); |
| json_object_object_del(jso, "digests"); |
| json_object_object_add(jso, "digests", jso_ary); |
| return TSS2_RC_SUCCESS; |
| |
| error: |
| if (jso_digest) |
| json_object_put(jso_digest); |
| if (jso_digest_type) |
| json_object_put(jso_digest_type); |
| if (jso_ary) |
| json_object_put(jso_ary); |
| return r; |
| } |
| |
| /** Callback for digest of old IMA format. |
| */ |
| static TSS2_RC |
| sha_digest_json_cb(UINT8 *digest, UINT8 * buffer, size_t *offset, json_object *jso, |
| IFAPI_IMA_TEMPLATE *template) { |
| TSS2_RC r; |
| UNUSED(template); |
| |
| LOGBLOB_TRACE(&buffer[*offset], TPM2_SHA1_DIGEST_SIZE, "IMA buffer"); |
| LOGBLOB_TRACE(digest, TPM2_SHA1_DIGEST_SIZE, "IMA digest"); |
| |
| if (jso && zero_digest(digest, TPM2_SHA1_DIGEST_SIZE) && |
| zero_digest(&buffer[*offset], TPM2_SHA1_DIGEST_SIZE)) { |
| r = set_ff_digest(jso); |
| return_if_error(r, "Set 0xff in digest."); |
| } |
| |
| *offset += TPM2_SHA1_DIGEST_SIZE; |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Get UINT32 size value from buffer and increase offset. |
| */ |
| UINT32 |
| get_size_from_buffer(UINT8 *buffer, size_t *offset, IFAPI_IMA_TEMPLATE *template) { |
| UINT32 size; |
| memcpy(&size, &buffer[*offset], sizeof(UINT32)); |
| *offset += sizeof(UINT32); |
| if (template->convert_to_big_endian) { |
| size = endian_swap_32(size); |
| } |
| return size; |
| } |
| |
| /** Callback for digest with name of used hash algorithm, |
| */ |
| static TSS2_RC |
| digest_with_hash_name_cb(UINT8 *digest, UINT8 *buffer, size_t *offset, json_object *jso, |
| IFAPI_IMA_TEMPLATE *template) { |
| TSS2_RC r; |
| char hash_alg[CRYPTO_MAX_ALG_NAME + 1] = { 0 }; |
| size_t alg_name_size; |
| const EVP_MD *md; |
| int digest_size; |
| UINT32 digest_buffer_size; |
| |
| digest_buffer_size = get_size_from_buffer(buffer, offset, template); |
| alg_name_size = strlen((char *)&buffer[*offset]) - 1; /**< strip : */ |
| if (alg_name_size > CRYPTO_MAX_ALG_NAME) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid hash name."); |
| } |
| memcpy(hash_alg, &buffer[*offset], alg_name_size); |
| md = EVP_get_digestbyname(hash_alg); |
| if (md == NULL) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid hash name."); |
| } |
| *offset += alg_name_size + 2; /**< skip : and '\0' */ |
| digest_size = EVP_MD_size(md); |
| if (alg_name_size + 2 + digest_size != digest_buffer_size) { |
| digest_buffer_size = endian_swap_32(digest_buffer_size); |
| /* Try with endian swap */ |
| if (alg_name_size + 2 + digest_size != digest_buffer_size) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid IMA binary format."); |
| } |
| } |
| LOGBLOB_TRACE(&buffer[*offset], digest_size, "IMA data_hash"); |
| |
| if (jso && zero_digest(digest, template->hash_size) && |
| zero_digest(&buffer[*offset], digest_size)) { |
| r = set_ff_digest(jso); |
| return_if_error(r, "Set 0xff in digest."); |
| } |
| *offset += digest_size; |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Callback to get digest with size field (UINT32). |
| */ |
| static TSS2_RC |
| signature_cb(UINT8 *digest, UINT8 *buffer, size_t *offset, json_object *jso, |
| IFAPI_IMA_TEMPLATE *template) { |
| UNUSED(digest); |
| UNUSED(jso); |
| UINT32 digest_size; |
| UNUSED(template); |
| |
| digest_size = get_size_from_buffer(buffer, offset, template); |
| LOGBLOB_TRACE(&buffer[*offset], digest_size, "IMA Signature:"); |
| *offset += digest_size; |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Callback to get null terminated name with size field (n-ng). |
| */ |
| static TSS2_RC eventname_ng_json_cb(UINT8 *digest, UINT8 *buffer, size_t *offset, json_object *jso, |
| IFAPI_IMA_TEMPLATE *template) { |
| size_t size; |
| UINT32 size_from_buffer; |
| UNUSED(digest); |
| UNUSED(jso); |
| |
| /* Get size from buffer with 0 Terminator. */ |
| size_from_buffer = get_size_from_buffer(buffer, offset, template); |
| size = strlen((const char *)&buffer[*offset]); |
| if (size != size_from_buffer - 1) { |
| size_from_buffer = endian_swap_32(size_from_buffer); |
| /* Try with endian swap */ |
| if (size != size_from_buffer - 1) { |
| return_error2(TSS2_FAPI_RC_BAD_VALUE, |
| "Invalid digest size, string length: %zu size from buffer: %" |
| PRIu32, size, size_from_buffer); |
| } |
| } |
| LOG_TRACE("IMA name: %s", (const char *)&buffer[*offset]); |
| template->name = (char *)&buffer[*offset]; |
| *offset += size + 1; /**< with 0 terminator */ |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Callback to get null terminated name (n). |
| */ |
| static TSS2_RC eventname_cb(UINT8 *digest, UINT8 *buffer, size_t *offset, json_object *jso, |
| IFAPI_IMA_TEMPLATE *template) { |
| size_t size; |
| UNUSED(digest); |
| UNUSED(jso); |
| |
| size = strlen((const char *)&buffer[*offset]); // TODO check |
| if (size > TCG_EVENT_NAME_LEN_MAX + 1) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Too long event name."); |
| } |
| LOG_TRACE("IMA name: %s", (const char *)&buffer[*offset]); |
| template->name = (char *)&buffer[*offset]; |
| *offset += size + 1; /**< with 0 terminator */ |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Callback to initialize the json event list. |
| */ |
| TSS2_RC |
| init_event_list_json_cb(json_object **jso) { |
| return_if_null(jso, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| if (!*jso) { |
| *jso = json_object_new_array(); |
| return_if_null(*jso, "Out of memory", TSS2_FAPI_RC_MEMORY); |
| } |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /** Callback to convert header of IMA template to JSON. |
| */ |
| TSS2_RC |
| event_header_json_cb( |
| size_t recnum, |
| UINT32 pcr, |
| const char* ima_type, |
| UINT8 *digest, |
| size_t digest_size, |
| json_object *jso_list, |
| json_object **jso) |
| { |
| TSS2_RC r; |
| json_object *jso_digest, *jso_digest_type, *jso_ary, *jso_content; |
| char *hash_name; |
| |
| *jso = json_object_new_object(); |
| return_if_null(*jso, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| |
| r = add_number_to_json(recnum, *jso, "recnum"); |
| return_if_error(r, "Add number to json object."); |
| |
| r = add_number_to_json(pcr, *jso, "pcr"); |
| return_if_error(r, "Add number to json object."); |
| |
| jso_digest = json_object_new_object(); |
| return_if_null(*jso, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| |
| jso_digest_type = NULL; |
| switch (digest_size) { |
| case TPM2_SHA1_DIGEST_SIZE: |
| hash_name = "sha1"; |
| break; |
| case TPM2_SHA256_DIGEST_SIZE: |
| hash_name = "sha256"; |
| break; |
| case TPM2_SHA384_DIGEST_SIZE: |
| hash_name = "sha384"; |
| break; |
| case TPM2_SHA512_DIGEST_SIZE: |
| hash_name = "sha512"; |
| break; |
| default: |
| return_error2(TSS2_FAPI_RC_BAD_VALUE, "Invalid hash size %zu", |
| digest_size); |
| } |
| |
| jso_digest_type = json_object_new_string (hash_name); |
| return_if_null(jso_digest_type, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| |
| json_object_object_add(jso_digest, "hashAlg", jso_digest_type); |
| |
| jso_ary = json_object_new_array(); |
| return_if_null(jso_ary, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| |
| json_object_array_add(jso_ary, jso_digest); |
| json_object_object_add(*jso, "digests", jso_ary); |
| |
| r = add_uint8_ary_to_json(digest, digest_size, jso_digest, "digest"); |
| return_if_error(r, "Add digest to json"); |
| |
| r = add_string_to_json("ima_template", *jso, CONTENT_TYPE); |
| return_if_error(r, "Add number to json object."); |
| |
| r = get_json_content(*jso, &jso_content); |
| return_if_error(r, "Get sub event"); |
| |
| r = add_string_to_json(ima_type, jso_content, "template_name"); |
| return_if_error(r, "Add number to json object."); |
| |
| json_object_array_add(jso_list, *jso); |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /* Type to store field data and the field callback */ |
| struct template_field { |
| const char *field_id; |
| TSS2_RC (*field_cb) (UINT8 *digest, UINT8 *buffer, size_t *offset, json_object *jso, |
| IFAPI_IMA_TEMPLATE *template); |
| }; |
| |
| /* Type for storing the IMA template descriptor */ |
| struct template_description { |
| char *ima_type; |
| char *format; |
| }; |
| |
| /* Callbacks to initialize result list and add events to the list */ |
| struct event_callbacks { |
| TSS2_RC (*init_list_cb) (json_object **jso); |
| TSS2_RC (*add_header_cb) (size_t recnum, |
| UINT32 pcr, |
| const char* ima_type, |
| UINT8 *digest, |
| size_t digest_size, |
| json_object *jso_list, |
| json_object **jso_current_event); |
| }; |
| |
| static struct event_callbacks event_callbacks = { |
| .init_list_cb = init_event_list_json_cb, |
| .add_header_cb = event_header_json_cb |
| }; |
| |
| /* |
| * Supported Descriptors and Template Fields. |
| */ |
| static struct template_description template_tab[] = { |
| { .ima_type = "ima", |
| .format = "d|n"}, |
| { .ima_type = "ima-ng", |
| .format = "d-ng|n-ng"}, |
| { .ima_type = "ima-sig", |
| .format = "d-ng|n-ng|sig"} |
| }; |
| |
| static struct template_field field_tab[] = { |
| { .field_id = "d", |
| .field_cb = sha_digest_json_cb }, |
| { .field_id = "n", |
| .field_cb = eventname_cb }, |
| { .field_id = "d-ng", |
| .field_cb = digest_with_hash_name_cb}, |
| { .field_id = "n-ng", |
| .field_cb = eventname_ng_json_cb}, |
| { .field_id = "sig", |
| .field_cb = signature_cb}, |
| }; |
| |
| /** Convert the IMA event to output format. |
| * |
| * Based on the event description in the ima template the appropriate callbacks are |
| * determined and executed to generate the ouput. |
| * |
| * @param[in] template The IMA event. |
| * @param[out] jso The output object. |
| * @param[out] name The name of the event object. |
| * @retval TSS2_RC_SUCCESS if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_VALUE If the current template foramt can't be processed. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory. |
| */ |
| static TSS2_RC |
| convert_ima_event_buffer( |
| IFAPI_IMA_TEMPLATE *template, |
| json_object *jso, |
| char **name) |
| { |
| TSS2_RC r; |
| size_t offset = 0; |
| size_t i, j; |
| char *copy_of_template_format = NULL; |
| char *copy_of_template_format_orig = NULL; |
| char *current_field; |
| struct template_description *template_desc = NULL; |
| size_t size_of_template_tab = sizeof(template_tab) / sizeof(template_tab[0]); |
| size_t size_of_field_tab = sizeof(field_tab) / sizeof(field_tab[0]); |
| json_object *jso_content; |
| |
| /* Search template decription corresponding to the template type. */ |
| for (i = 0; i < size_of_template_tab; i++) { |
| if (strcmp(template->ima_type, template_tab[i].ima_type) == 0) { |
| template_desc = template_tab + i; |
| break; |
| } |
| } |
| |
| if (i == size_of_template_tab) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Ima template type not supported."); |
| } |
| |
| copy_of_template_format = strdup(template_desc->format); |
| /* Save pointer for cleanup. */ |
| copy_of_template_format_orig = copy_of_template_format; |
| goto_if_null(copy_of_template_format, "Out of memory.", TSS2_FAPI_RC_MEMORY, error); |
| /* Loop over template fields separated by | */ |
| for (i = 0; (current_field = strsep(©_of_template_format, "|")) != NULL; i++) { |
| struct template_field *field = NULL; |
| |
| for (j = 0; j < size_of_field_tab; j++) { |
| if (!strcmp(current_field, field_tab[j].field_id)) { |
| field = field_tab + j; |
| break; |
| } |
| } |
| |
| goto_if_null2(field, "Unknown field %s", r, TSS2_FAPI_RC_BAD_VALUE, error, current_field); |
| |
| /* Convert the IMA data with the found callback. */ |
| r = field->field_cb(&template->header.digest[0], |
| template->event_buffer, &offset, jso, template); |
| *name= template->name; |
| goto_if_error(r, "Get field", error); |
| } |
| if (jso) { |
| r = get_json_content(jso, &jso_content); |
| goto_if_error(r, "Get sub event", error); |
| |
| r = add_uint8_ary_to_json(template->event_buffer, template->event_size, jso_content, "template_data"); |
| goto_if_error(r, "Create data to be hashed", error); |
| } |
| |
| SAFE_FREE(copy_of_template_format_orig); |
| return TSS2_RC_SUCCESS; |
| error: |
| SAFE_FREE(copy_of_template_format_orig); |
| return r; |
| } |
| |
| /** Get one event from the event file. |
| * |
| * Retrieve the part of the event behind the fixed event header from the |
| * event file. |
| * |
| * @param[in,out] template The IMA event with the already read event header. |
| * the storage for the rest of the event will be allocated |
| * and stored in the template. |
| * @param[in] fp The event stream. |
| * |
| * @retval TSS2_RC_SUCCESS if the function call was a success. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory. |
| */ |
| static TSS2_RC |
| read_event_buffer(IFAPI_IMA_TEMPLATE *template, FILE *fp) |
| { |
| bool old_ima_format; |
| int size, rsize; |
| |
| /* Check IMA legacy format. */ |
| if (strcmp(template->ima_type, "ima") == 0) { |
| old_ima_format = true; |
| /* Size is fixed for old IMA format. */ |
| template->event_size = TPM2_SHA1_DIGEST_SIZE + TCG_EVENT_NAME_LEN_MAX + 1; |
| size = TPM2_SHA1_DIGEST_SIZE; |
| } else { |
| old_ima_format = false; |
| /* For the new IMA format get size from event. */ |
| rsize = fread(&template->event_size, sizeof(UINT32), 1, fp); |
| if (rsize != 1) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid ima data"); |
| } |
| if (template->convert_to_big_endian) { |
| template->event_size = endian_swap_32(template->event_size); |
| } |
| size = template->event_size; |
| } |
| |
| template->event_buffer = calloc(template->event_size, sizeof(UINT8)); |
| return_if_null(template->event_buffer, "Out of memory", TSS2_FAPI_RC_MEMORY); |
| |
| rsize = fread(template->event_buffer, size, 1, fp); |
| if (rsize != 1) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid ima data"); |
| } |
| if (old_ima_format) { |
| UINT32 field_len; |
| |
| rsize = fread(&field_len, sizeof(UINT32), 1, fp); |
| if (rsize != 1) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid ima data"); |
| } |
| if (template->convert_to_big_endian) { |
| field_len = endian_swap_32(field_len); |
| } |
| if (field_len > template->event_size - TPM2_SHA1_DIGEST_SIZE) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid ima data"); |
| } |
| rsize = fread(template->event_buffer +TPM2_SHA1_DIGEST_SIZE, |
| field_len, 1, fp); |
| if (rsize != 1) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid ima data"); |
| } |
| } |
| return TSS2_RC_SUCCESS; |
| } |
| |
| size_t read_ima_header(IFAPI_IMA_TEMPLATE *template, FILE *fp, TSS2_RC *rc) |
| { |
| /* header: pcr register, sha1 digest, size field ima type string, start of ima type */ |
| size_t header_size = sizeof(UINT32) + TPM2_SHA1_DIGEST_SIZE + sizeof(UINT32) + 3; |
| size_t size, rsize; |
| size_t pos_ima_type = header_size - 3 - sizeof(UINT32); |
| |
| *rc = TSS2_RC_SUCCESS; |
| |
| size = fread(&template->header, header_size, 1, fp); |
| if (size == 0) { |
| return size; |
| } |
| |
| template->convert_to_big_endian = need_to_convert_to_big_endian(template); |
| |
| if (memcmp(&template->header.digest[pos_ima_type], "ima", 3) == 0) { |
| /* Start of IMA type string found. */ |
| memcpy(&template->ima_type_size, |
| &template->header.digest[pos_ima_type - sizeof(UINT32)], |
| sizeof(UINT32)); |
| if (template->convert_to_big_endian) { |
| template->ima_type_size = endian_swap_32(template->ima_type_size); |
| } |
| memcpy(&template->ima_type[0], "ima", 3); |
| /* Get the description of the IMA event. */ |
| if (template->ima_type_size < 3 || |
| template->ima_type_size >= TCG_EVENT_NAME_LEN_MAX) { |
| LOG_ERROR("Invalid ima data"); |
| *rc = TSS2_FAPI_RC_BAD_VALUE; |
| return 0; |
| } |
| size = template->ima_type_size - 3; |
| if (size > 0) { |
| rsize = fread(&template->ima_type[3], size, 1, fp); |
| if (rsize != 1) { |
| LOG_ERROR("Invalid ima data"); |
| *rc = TSS2_FAPI_RC_BAD_VALUE; |
| return 0; |
| } |
| template->ima_type[template->ima_type_size] = '\0'; |
| } else { |
| template->ima_type[3] = '\0'; |
| } |
| template->hash_alg = TPM2_ALG_SHA1; |
| template->hash_size = TPM2_SHA1_DIGEST_SIZE; |
| return header_size; |
| } |
| return 0; |
| } |
| |
| /** Read ima eventlog and create JSON list of events. |
| * |
| * @param[in] filename The filname of the IMA event file. |
| * @param[in] pcrList The list of PCRs that are to be quoted |
| * @param[in] pcrListSize The size of pcrList in bytes |
| * @param[out] The event_list in JSON format. |
| * @retval TSS2_RC_SUCCESS if the function call was a success. |
| * @retval TSS2_FAPI_RC_IO_ERROR If a event cannot be read. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| */ |
| TSS2_RC |
| ifapi_read_ima_event_log( |
| const char *filename, |
| const uint32_t *pcrList, |
| size_t pcrListSize, |
| json_object **jso_list) { |
| TSS2_RC r; |
| FILE *fp = NULL; |
| IFAPI_IMA_TEMPLATE template = { 0 }; |
| size_t recnum = 0, i; |
| json_object *jso_current_event = NULL;; |
| bool add_event; |
| uint32_t pcr = 0; |
| |
| return_if_null(jso_list, "Bad reference.", TSS2_FAPI_RC_BAD_VALUE); |
| template.event_buffer = NULL; |
| |
| OpenSSL_add_all_digests(); |
| fp = fopen(filename, "r"); |
| goto_if_null2(fp, "Could not open: %s", r, TSS2_FAPI_RC_IO_ERROR, error, filename); |
| LOG_INFO("IMA read file: %s", filename); |
| |
| r = event_callbacks.init_list_cb(jso_list); |
| goto_if_error(r, "Initialize event list.", error); |
| |
| /* While the event header with fixed size can be read. */ |
| while (read_ima_header(&template, fp, &r)) { |
| if (r) { |
| return_error(r, "Invalid ima data.") |
| } |
| add_event = true; |
| if (!template.ima_type_size || |
| template.ima_type_size > TCG_EVENT_NAME_LEN_MAX) { |
| goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid ima type size", error); |
| } |
| |
| /* Check whether IMA PCR is member of pcrList. */ |
| for (i = 0; i < pcrListSize; i++) { |
| pcr = template.header.pcr; |
| if (template.convert_to_big_endian) { |
| pcr= endian_swap_32(pcr); |
| } |
| if (pcrList[i] == pcr) |
| break; |
| } |
| if (i == pcrListSize) { |
| /* PCR is not used. */ |
| add_event = false; |
| } |
| |
| if (add_event) { |
| r = event_callbacks.add_header_cb(recnum, pcr, |
| template.ima_type, template.header.digest, |
| template.hash_size, |
| *jso_list, &jso_current_event); |
| goto_if_error(r, "Add header to event list.", error); |
| |
| recnum += 1; |
| } |
| |
| /* Read the rest of the event. */ |
| r = read_event_buffer(&template, fp); |
| goto_if_error(r, "Read event buffer.", error); |
| |
| if (add_event) { |
| char *name; |
| r = convert_ima_event_buffer(&template, jso_current_event, &name); |
| goto_if_error(r, "Create json event.", error); |
| } |
| SAFE_FREE(template.event_buffer); |
| |
| } |
| fclose(fp); |
| return TSS2_RC_SUCCESS; |
| |
| error: |
| SAFE_FREE(template.event_buffer); |
| if (fp) |
| fclose(fp); |
| if (*jso_list) |
| json_object_put(*jso_list); |
| return r; |
| } |
| |
| static char *field_IFAPI_IMA_EVENT_tab[] = { |
| "template_value", |
| "template_data", |
| "template_name" |
| }; |
| |
| /** Deserialize a IFAPI_IMA_EVENT_TYPE json object. |
| * |
| * @param[in] jso the json object to be deserialized. |
| * @param[out] out the deserialzed binary object. |
| * @retval TSS2_RC_SUCCESS if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if the json object can't be deserialized. |
| */ |
| TSS2_RC |
| ifapi_json_IFAPI_IMA_EVENT_TYPE_deserialize(json_object *jso, IFAPI_IMA_EVENT_TYPE *out) |
| { |
| LOG_TRACE("call"); |
| return ifapi_json_IFAPI_IMA_EVENT_TYPE_deserialize_txt(jso, out); |
| } |
| |
| typedef struct { |
| IFAPI_IMA_EVENT_TYPE in; |
| char *name; |
| } IFAPI_IFAPI_IMA_EVENT_TYPE_ASSIGN; |
| |
| static IFAPI_IFAPI_IMA_EVENT_TYPE_ASSIGN deserialize_IFAPI_IMA_EVENT_TYPE_tab[] = { |
| { IFAPI_IMA_EVENT_TAG_IMA, "ima" }, |
| { IFAPI_IMA_EVENT_TAG_NG, "ima-ng" }, |
| { IFAPI_IMA_EVENT_TAG_SIG, "ima-sig" }, |
| }; |
| |
| /** Deserialize a json object of type IFAPI_IMA_EVENT_TYPE. |
| * |
| * @param[in] jso the json object to be deserialized. |
| * @param[out] out the deserialzed binary object. |
| * @retval TSS2_RC_SUCCESS if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if the json object can't be deserialized. |
| */ |
| TSS2_RC |
| ifapi_json_IFAPI_IMA_EVENT_TYPE_deserialize_txt(json_object *jso, |
| IFAPI_IMA_EVENT_TYPE *out) |
| { |
| LOG_TRACE("call"); |
| const char *token = json_object_get_string(jso); |
| size_t i; |
| size_t n = sizeof(deserialize_IFAPI_IMA_EVENT_TYPE_tab) / |
| sizeof(deserialize_IFAPI_IMA_EVENT_TYPE_tab[0]); |
| size_t size = strlen(token); |
| for (i = 0; i < n; i++) { |
| if (strncasecmp(&token[0], |
| &deserialize_IFAPI_IMA_EVENT_TYPE_tab[i].name[0], |
| size) == 0) { |
| *out = deserialize_IFAPI_IMA_EVENT_TYPE_tab[i].in; |
| return TSS2_RC_SUCCESS; |
| } |
| } |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Undefined constant."); |
| } |
| |
| /** Deserialize a IFAPI_IMA_EVENT json object. |
| * |
| * @param[in] jso the json object to be deserialized. |
| * @param[out] out the deserialzed binary object. |
| * @retval TSS2_RC_SUCCESS if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_VALUE if the json object can't be deserialized. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. |
| * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. |
| */ |
| TSS2_RC |
| ifapi_json_IFAPI_IMA_EVENT_deserialize(json_object *jso, IFAPI_IMA_EVENT *out) |
| { |
| json_object *jso2; |
| TSS2_RC r; |
| |
| LOG_TRACE("call"); |
| return_if_null(out, "Bad reference.", TSS2_FAPI_RC_BAD_REFERENCE); |
| |
| ifapi_check_json_object_fields(jso, &field_IFAPI_IMA_EVENT_tab[0], |
| SIZE_OF_ARY(field_IFAPI_IMA_EVENT_tab)); |
| |
| if (!ifapi_get_sub_object(jso, "template_name", &jso2)) { |
| LOG_ERROR("Field \"template_name\" not found."); |
| return TSS2_FAPI_RC_BAD_VALUE; |
| } |
| r = ifapi_json_IFAPI_IMA_EVENT_TYPE_deserialize(jso2, &out->template_name); |
| return_if_error(r, "Bad value for field \"template_name\"."); |
| |
| if (!ifapi_get_sub_object(jso, "template_data", &jso2)) { |
| if (!ifapi_get_sub_object(jso, "template_value", &jso2)) { |
| LOG_ERROR("Field \"template_data\" not found."); |
| return TSS2_FAPI_RC_BAD_VALUE; |
| } |
| } |
| r = ifapi_json_UINT8_ARY_deserialize(jso2, &out->template_value); |
| return_if_error(r, "Bad value for field \"template_data \"."); |
| |
| LOG_TRACE("true"); |
| return TSS2_RC_SUCCESS; |
| } |
| |
| TSS2_RC |
| ifapi_get_ima_eventname(IFAPI_IMA_EVENT *ima_event, char **name) |
| { |
| TSS2_RC r; |
| IFAPI_IMA_TEMPLATE template; |
| size_t i; |
| size_t n = SIZE_OF_ARY(deserialize_IFAPI_IMA_EVENT_TYPE_tab); |
| |
| memset(&template, 0, sizeof(template)); |
| for (i = 0; i < n; i++) { |
| if (deserialize_IFAPI_IMA_EVENT_TYPE_tab[i].in == |
| ima_event->template_name) { |
| char *tab_name = deserialize_IFAPI_IMA_EVENT_TYPE_tab[i].name; |
| memcpy(&template.ima_type, tab_name, strlen(tab_name)+1); |
| break; |
| } |
| } |
| if (i >= n) { |
| return_error(TSS2_FAPI_RC_BAD_VALUE, "Undefined constant."); |
| } |
| template.event_size = ima_event->template_value.size; |
| template.event_buffer = &ima_event->template_value.buffer[0]; |
| r = convert_ima_event_buffer(&template, NULL, name); |
| return_if_error(r, "Parsing of IMA template failed."); |
| |
| return TSS2_RC_SUCCESS; |
| } |