/* SPDX-License-Identifier: BSD-2-Clause */ /******************************************************************************* * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG * All rights reserved. *******************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include "ifapi_helpers.h" #include "ifapi_eventlog.h" #include "ifapi_json_serialize.h" #define LOGMODULE fapi #include "util/log.h" #include "util/aux_util.h" #include "ifapi_macros.h" /** Initialize the eventlog module of FAPI. * * @param[in,out] eventlog The context area for the eventlog. * @param[in] log_dir The directory where to put the eventlog data. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR if creation of log_dir failed or log_dir is not writable. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. */ TSS2_RC ifapi_eventlog_initialize( IFAPI_EVENTLOG *eventlog, const char *log_dir) { check_not_null(eventlog); check_not_null(log_dir); TSS2_RC r; r = ifapi_io_check_create_dir(log_dir); return_if_error2(r, "Directory check/creation failed for %s", log_dir); eventlog->log_dir = strdup(log_dir); return_if_null(eventlog->log_dir, "Out of memory.", TSS2_FAPI_RC_MEMORY); return TSS2_RC_SUCCESS; } /** Retrieve the eventlog for a given list of pcrs using asynchronous io. * * Call ifapi_eventlog_get_finish to retrieve the results. * * @param[in,out] eventlog The context area for the eventlog. * @param[in,out] io The context area for the asynchronous io module. * @param[in] pcrList The list of PCR indices to retrieve the log for. * @param[in] pcrListSize The size of pcrList. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR if creation of log_dir failed or log_dir is not writable. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_eventlog_get_async( IFAPI_EVENTLOG *eventlog, IFAPI_IO *io, const TPM2_HANDLE *pcrList, size_t pcrListSize) { check_not_null(eventlog); check_not_null(io); check_not_null(pcrList); if (pcrListSize > TPM2_MAX_PCRS) { LOG_ERROR("pcrList too long %zi > %i", pcrListSize, TPM2_MAX_PCRS); return TSS2_FAPI_RC_BAD_VALUE; } LOG_TRACE("called for pcrListSize=%zi", pcrListSize); memcpy(&eventlog->pcrList, pcrList, pcrListSize * sizeof(TPM2_HANDLE)); eventlog->pcrListSize = pcrListSize; eventlog->pcrListIdx = 0; eventlog->log = json_object_new_array(); return_if_null(eventlog->log, "Out of memory", TSS2_FAPI_RC_MEMORY); return TSS2_RC_SUCCESS; } /** Retrieve the eventlog for a given list of pcrs using asynchronous io. * * Call after ifapi_eventlog_get_async. * * @param[in,out] eventlog The context area for the eventlog. * @param[in,out] io The context area for the asynchronous io module. * @param[out] log The event log for the requested PCRs in JSON format * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR if creation of log_dir failed or log_dir is not writable. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_TRY_AGAIN if the I/O operation is not finished yet and this function needs * to be called again. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous * operation already pending. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_eventlog_get_finish( IFAPI_EVENTLOG *eventlog, IFAPI_IO *io, char **log) { /* eventlog parameter currently not used */ check_not_null(eventlog); check_not_null(io); check_not_null(log); TSS2_RC r; char *event_log_file, *logstr; json_object *logpart, *event; LOG_TRACE("called"); loop: /* If we're dune with adding all eventlogs to the json array, we can serialize it and return it to the caller. */ if (eventlog->pcrListIdx >= eventlog->pcrListSize) { LOG_TRACE("Done reading pcrLog"); *log = strdup(json_object_to_json_string_ext(eventlog->log, JSON_C_TO_STRING_PRETTY)); check_oom(*log); json_object_put(eventlog->log); eventlog->log = NULL; eventlog->state = IFAPI_EVENTLOG_STATE_INIT; return TSS2_RC_SUCCESS; } switch (eventlog->state) { statecase(eventlog->state, IFAPI_EVENTLOG_STATE_INIT) /* Construct the filename for the eventlog file */ r = ifapi_asprintf(&event_log_file, "%s/%s%i", eventlog->log_dir, IFAPI_PCR_LOG_FILE, eventlog->pcrList[eventlog->pcrListIdx]); return_if_error(r, "Out of memory."); if (!ifapi_io_path_exists(event_log_file)) { LOG_DEBUG("No event log for pcr %i", eventlog->pcrList[eventlog->pcrListIdx]); SAFE_FREE(event_log_file); eventlog->pcrListIdx += 1; goto loop; } /* Initiate the reading of the eventlog file */ r = ifapi_io_read_async(io, event_log_file); free(event_log_file); if (r) { LOG_DEBUG("No event log for pcr %i", eventlog->pcrList[eventlog->pcrListIdx]); eventlog->pcrListIdx += 1; goto loop; } fallthrough; statecase(eventlog->state, IFAPI_EVENTLOG_STATE_READING) /* Finish the reading of the eventlog file and return it directly to the output parameter */ r = ifapi_io_read_finish(io, (uint8_t **)&logstr, NULL); return_try_again(r); return_if_error(r, "read_finish failed"); logpart = json_tokener_parse(logstr); SAFE_FREE(logstr); return_if_null(log, "JSON parsing error", TSS2_FAPI_RC_BAD_VALUE); /* Append the log-entry from logpart to the eventlog */ json_type jso_type = json_object_get_type(logpart); if (jso_type != json_type_array) { /* libjson-c does not deliver an array if array has only one element */ json_object_array_add(eventlog->log, logpart); } else { /* Iterate through the array of logpart and add each item to the eventlog */ /* The return type of json_object_array_length() was changed, thus the case */ for (int i = 0; i < (int)json_object_array_length(logpart); i++) { event = json_object_array_get_idx(logpart, i); /* Increment the refcount of event so it does not get freed on put(logpart) below */ json_object_get(event); json_object_array_add(eventlog->log, event); } json_object_put(logpart); } eventlog->pcrListIdx += 1; eventlog->state = IFAPI_EVENTLOG_STATE_INIT; goto loop; statecasedefault(eventlog->state); } return TSS2_RC_SUCCESS; } /** Append an event to the existing event log. * * Call ifapi_eventlog_append_finish to finalize this operation. * * @param[in,out] eventlog The context area for the eventlog. * @param[in,out] io The context area for the asynchronous io module. * @param[in] event The event to be appended to the eventlog. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR if creation of log_dir failed or log_dir is not writable. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous * operation already pending. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_eventlog_append_async( IFAPI_EVENTLOG *eventlog, IFAPI_IO *io, const IFAPI_EVENT *event) { check_not_null(eventlog); check_not_null(io); check_not_null(event); TSS2_RC r; char *event_log_file; if (eventlog->state != IFAPI_EVENTLOG_STATE_INIT) { LOG_ERROR("Wrong state: %i", eventlog->state); return TSS2_FAPI_RC_BAD_SEQUENCE; } eventlog->event = *event; /* Construct the filename for the eventlog file */ r = ifapi_asprintf(&event_log_file, "%s/%s%i", eventlog->log_dir, IFAPI_PCR_LOG_FILE, event->pcr); return_if_error(r, "Out of memory."); /* Initiate the reading of the eventlog file */ r = ifapi_io_read_async(io, event_log_file); if (r) { LOG_DEBUG("Eventlog file %s could not be opened, creating...", event_log_file); free(event_log_file); eventlog->state = IFAPI_EVENTLOG_STATE_APPENDING; return TSS2_RC_SUCCESS; } free(event_log_file); eventlog->state = IFAPI_EVENTLOG_STATE_READING; return TSS2_RC_SUCCESS; } /** Append an event to the existing event log. * * Call after ifapi_eventlog_get_async. * * @param[in,out] eventlog The context area for the eventlog. * @param[in,out] io The context area for the asynchronous io module. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR if creation of log_dir failed or log_dir is not writable. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_TRY_AGAIN if the I/O operation is not finished yet and this function needs * to be called again. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous * operation already pending. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_eventlog_append_finish( IFAPI_EVENTLOG *eventlog, IFAPI_IO *io) { check_not_null(eventlog); check_not_null(io); TSS2_RC r; char *logstr = NULL, *event_log_file; const char *logstr2 = NULL; json_object *log, *event = NULL; switch (eventlog->state) { statecase(eventlog->state, IFAPI_EVENTLOG_STATE_READING) /* Finish the reading of the eventlog file and return it directly to the output parameter */ r = ifapi_io_read_finish(io, (uint8_t **)&logstr, NULL); return_try_again(r); return_if_error(r, "read_finish failed"); fallthrough; statecase(eventlog->state, IFAPI_EVENTLOG_STATE_APPENDING) /* If a log was read, we deserialize it to JSON. Otherwise we start a new log. */ if (logstr) { log = json_tokener_parse(logstr); SAFE_FREE(logstr); return_if_null(log, "JSON parsing error", TSS2_FAPI_RC_BAD_VALUE); /* libjson-c does not deliver an array if array has only one element */ json_type jso_type = json_object_get_type(log); if (jso_type != json_type_array) { json_object *json_array = json_object_new_array(); json_object_array_add(json_array, log); log = json_array; } } else { log = json_object_new_array(); return_if_null(log, "Out of memory", TSS2_FAPI_RC_MEMORY); } /* Extend the eventlog with the data */ eventlog->event.recnum = json_object_array_length(log) + 1; r = ifapi_json_IFAPI_EVENT_serialize(&eventlog->event, &event); if (r) { json_object_put(log); LOG_ERROR("Error serializing event data"); return TSS2_FAPI_RC_GENERAL_FAILURE; } json_object_array_add(log, event); logstr2 = json_object_to_json_string_ext(log, JSON_C_TO_STRING_PRETTY); /* Construct the filename for the eventlog file */ r = ifapi_asprintf(&event_log_file, "%s/%s%i", eventlog->log_dir, IFAPI_PCR_LOG_FILE, eventlog->event.pcr); return_if_error(r, "Out of memory."); /* Start writing the eventlog back to disk */ r = ifapi_io_write_async(io, event_log_file, (uint8_t *) logstr2, strlen(logstr2)); free(event_log_file); json_object_put(log); /* this also frees logstr2 */ return_if_error(r, "write_async failed"); fallthrough; statecase(eventlog->state, IFAPI_EVENTLOG_STATE_WRITING) /* Finish writing the eventlog */ r = ifapi_io_write_finish(io); return_try_again(r); return_if_error(r, "read_finish failed"); eventlog->state = IFAPI_EVENTLOG_STATE_INIT; break; statecasedefault(eventlog->state); } return TSS2_RC_SUCCESS; } /** Free allocated memory for an ifapi event. * * @param[in,out] event The structure to be cleaned up. */ void ifapi_cleanup_event(IFAPI_EVENT * event) { if (event != NULL) { if (event->type == IFAPI_IMA_EVENT_TAG) { SAFE_FREE(event->sub_event.ima_event.eventName); } else if (event->type == IFAPI_TSS_EVENT_TAG) { SAFE_FREE(event->sub_event.tss_event.event); } } }