| /* SPDX-License-Identifier: BSD-2-Clause */ |
| /******************************************************************************* |
| * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG |
| * All rights reserved. |
| *******************************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #ifndef NO_DL |
| #include <dlfcn.h> |
| #endif /* NO_DL */ |
| |
| #include "tss2_tcti.h" |
| #include "tss2_tctildr.h" |
| #include "tss2_esys.h" |
| #include "tss2_fapi.h" |
| #include "fapi_int.h" |
| #include "fapi_util.h" |
| #include "ifapi_json_deserialize.h" |
| #define LOGMODULE fapi |
| #include "util/log.h" |
| #include "util/aux_util.h" |
| |
| /** One-Call function for Fapi_Initialize |
| * |
| * Initializes a FAPI_CONTEXT that holds all the state and metadata information |
| * during an interaction with the TPM. |
| * |
| * @param[out] context The FAPI_CONTEXT |
| * @param[in] uri Unused in this version of the FAPI. Must be NULL |
| * |
| * @retval TSS2_RC_SUCCESS: if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. |
| * @retval TSS2_FAPI_RC_BAD_VALUE: if uri is not NULL. |
| * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and |
| * this function needs to be called again. |
| * @retval TSS2_FAPI_RC_BAD_SEQUENCE if the context has an asynchronous |
| * operation already pending. |
| * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. |
| * @retval TSS2_FAPI_RC_BAD_PATH if a path is used in inappropriate context |
| * or contains illegal characters. |
| * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. |
| */ |
| TSS2_RC |
| Fapi_Initialize( |
| FAPI_CONTEXT **context, |
| char const *uri) |
| { |
| LOG_TRACE("called for context:%p", context); |
| |
| TSS2_RC r = TSS2_RC_SUCCESS; |
| |
| /* Check for NULL parameters */ |
| check_not_null(context); |
| if (uri != NULL) { |
| LOG_ERROR("uri is not NULL"); |
| return TSS2_FAPI_RC_BAD_VALUE; |
| } |
| |
| r = Fapi_Initialize_Async(context, uri); |
| return_if_error(r, "FAPI Async call initialize"); |
| check_oom(*context); |
| |
| do { |
| /* We wait for file I/O to be ready if the FAPI state automata |
| are in a file I/O state. */ |
| r = ifapi_io_poll(&(*context)->io); |
| return_if_error(r, "Something went wrong with IO polling"); |
| |
| /* Repeatedly call the finish function, until FAPI has transitioned |
| through all execution stages / states of this invocation. */ |
| r = Fapi_Initialize_Finish(context); |
| } while (base_rc(r) == TSS2_BASE_RC_TRY_AGAIN); |
| |
| LOG_TRACE("finished"); |
| return r; |
| } |
| |
| /** Asynchronous function for Fapi_Initialize |
| * |
| * Initializes a FAPI_CONTEXT that holds all the state and metadata information |
| * during an interaction with the TPM. |
| * |
| * Call Fapi_Initialize to finish the execution of this command. |
| * |
| * @param[out] context The FAPI_CONTEXT |
| * @param[in] uri Unused in this version of the FAPI. Must be NULL |
| * |
| * @retval TSS2_RC_SUCCESS: if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. |
| * @retval TSS2_FAPI_RC_BAD_VALUE: if uri is not NULL. |
| * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| */ |
| TSS2_RC |
| Fapi_Initialize_Async( |
| FAPI_CONTEXT **context, |
| char const *uri) |
| { |
| LOG_TRACE("called for context:%p", context); |
| LOG_TRACE("uri: %s", uri); |
| |
| TSS2_RC r = TSS2_RC_SUCCESS; |
| |
| /* Check for NULL parameters */ |
| check_not_null(context); |
| if (uri != NULL) { |
| LOG_ERROR("uri is not NULL"); |
| return TSS2_FAPI_RC_BAD_VALUE; |
| } |
| |
| *context = NULL; |
| |
| /* Allocate memory for the FAPI context |
| * After this errors must jump to cleanup_return instead of returning. */ |
| *context = calloc(1, sizeof(FAPI_CONTEXT)); |
| return_if_null(*context, "Out of memory.", TSS2_FAPI_RC_MEMORY); |
| memset(*context, 0, sizeof(FAPI_CONTEXT)); |
| |
| /* Initialize the context */ |
| r = ifapi_config_initialize_async(&(*context)->io); |
| goto_if_error(r, "Could not initialize FAPI context", cleanup_return); |
| |
| memset(&(*context)->cmd.Initialize, 0, sizeof(IFAPI_INITIALIZE)); |
| |
| /* Initialize the context state for this operation. */ |
| (*context)->state = INITIALIZE_READ; |
| |
| cleanup_return: |
| if (r) |
| SAFE_FREE(*context); |
| LOG_TRACE("finished"); |
| return r; |
| } |
| |
| /** Asynchronous finish function for Fapi_Initialize |
| * |
| * This function should be called after a previous Fapi_Initialize_Async. |
| * |
| * @param[out] context The FAPI_CONTEXT |
| * |
| * @retval TSS2_RC_SUCCESS: if the function call was a success. |
| * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. |
| * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected. |
| * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous |
| * operation already pending. |
| * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved. |
| * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for |
| * internal operations or return parameters. |
| * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet |
| * complete. Call this function again later. |
| * @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_PATH if a path is used in inappropriate context |
| * or contains illegal characters. |
| * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. |
| */ |
| TSS2_RC |
| Fapi_Initialize_Finish( |
| FAPI_CONTEXT **context) |
| { |
| LOG_TRACE("called for context:%p", context); |
| |
| TSS2_RC r; |
| TPMI_YES_NO moreData; |
| TSS2_TCTI_CONTEXT *fapi_tcti = NULL; |
| TPMS_TIME_INFO *currentTime = NULL; |
| IFAPI_OBJECT pkey_object; |
| size_t i; |
| |
| /* Check for NULL parameters */ |
| check_not_null(context); |
| check_not_null(*context); |
| |
| /* Helpful alias pointers */ |
| TPMS_CAPABILITY_DATA **capability = &(*context)->cmd.Initialize.capability; |
| IFAPI_INITIALIZE * command = &(*context)->cmd.Initialize; |
| |
| switch ((*context)->state) { |
| statecase((*context)->state, INITIALIZE_READ); |
| /* This is the entry point; finishing the initialization of the config module. */ |
| r = ifapi_config_initialize_finish(&(*context)->io, &(*context)->config); |
| return_try_again(r); |
| goto_if_error(r, "Could not finish initialization", cleanup_return); |
| |
| /* Initialize the event log module. */ |
| r = ifapi_eventlog_initialize(&((*context)->eventlog), (*context)->config.log_dir); |
| goto_if_error(r, "Initializing eventlog module", cleanup_return); |
| |
| /* Initialize the keystore. */ |
| r = ifapi_keystore_initialize(&((*context)->keystore), |
| (*context)->config.keystore_dir, |
| (*context)->config.user_dir, |
| (*context)->config.profile_name); |
| goto_if_error2(r, "Keystore could not be initialized.", cleanup_return); |
| |
| /* Initialize the policy store. */ |
| /* Policy directory will be placed in keystore dir */ |
| r = ifapi_policy_store_initialize(&((*context)->pstore), |
| (*context)->config.keystore_dir); |
| goto_if_error2(r, "Keystore could not be initialized.", cleanup_return); |
| |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_INIT_TCTI); |
| if (strcasecmp((*context)->config.tcti, "none") == 0) { |
| /* FAPI will be used in none TPM mode */ |
| (*context)->esys = NULL; |
| (*context)->state = INITIALIZE_READ_PROFILE_INIT; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| } |
| |
| /* Call for the TctiLdr to initialize a TCTI context given the config |
| from the FAPI config module. */ |
| r = Tss2_TctiLdr_Initialize((*context)->config.tcti, &fapi_tcti); |
| goto_if_error(r, "Initializing TCTI.", cleanup_return); |
| |
| /* Initialize an ESYS context using this Tcti. */ |
| r = Esys_Initialize(&((*context)->esys), fapi_tcti, NULL); |
| goto_if_error(r, "Initialize esys context.", cleanup_return); |
| |
| /* Call Startup on the TPM. */ |
| r = Esys_Startup((*context)->esys, TPM2_SU_CLEAR); |
| if (r != TSS2_RC_SUCCESS && r != TPM2_RC_INITIALIZE) { |
| LOG_ERROR("Esys_Startup FAILED! Response Code : 0x%x", r); |
| return r; |
| } |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_GET_CAP); |
| /* Retrieve the maximal value for transfer of nv data from the TPM. */ |
| r = Esys_GetCapability_Async((*context)->esys, ESYS_TR_NONE, ESYS_TR_NONE, |
| ESYS_TR_NONE, |
| TPM2_CAP_TPM_PROPERTIES, TPM2_PT_NV_BUFFER_MAX, 1); |
| goto_if_error(r, "Error json deserialize", cleanup_return); |
| |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_WAIT_FOR_CAP); |
| r = Esys_GetCapability_Finish((*context)->esys, &moreData, capability); |
| return_try_again(r); |
| goto_if_error(r, "Get capability data.", cleanup_return); |
| |
| /* Check if the TPM returns the NV_BUFFER_MAX value. */ |
| if ((*capability)->data.tpmProperties.count == 1 && |
| (*capability)->data.tpmProperties.tpmProperty[0].property == |
| TPM2_PT_NV_BUFFER_MAX) { |
| (*context)->nv_buffer_max = (*capability)->data.tpmProperties.tpmProperty[0].value; |
| /* FAPI also contains an upper limit on the NV_MAX_BUFFER size. This is |
| useful for vTPMs that could in theory allow for several Megabytes of |
| max transfer buffer sizes. */ |
| if ((*context)->nv_buffer_max > IFAPI_MAX_BUFFER_SIZE) |
| (*context)->nv_buffer_max = IFAPI_MAX_BUFFER_SIZE; |
| } else { |
| /* Note that for some time it was legal for a TPM to not return this value. |
| in that case FAPI falls back to 64 bytes for NV_BUFFER_MAX that all TPMs |
| must support. This slows down communication for NV read and write but |
| ensures that data can be exchanged with the TPM. */ |
| (*context)->nv_buffer_max = 64; |
| } |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_READ_PROFILE_INIT); |
| /* Initialize the proviles module that loads cryptographic profiles. |
| The default profile is taken from config. */ |
| r = ifapi_profiles_initialize_async(&(*context)->profiles, &(*context)->io, |
| (*context)->config.profile_dir, |
| (*context)->config.profile_name); |
| return_if_error(r, "Read profile"); |
| |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_READ_PROFILE); |
| r = ifapi_profiles_initialize_finish(&(*context)->profiles, &(*context)->io); |
| FAPI_SYNC(r, "Read profile.", cleanup_return); |
| |
| if ((*context)->esys) { |
| r = Esys_ReadClock_Async((*context)->esys, |
| ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE); |
| goto_if_error(r, "ReadClock_Async.", cleanup_return); |
| } else { |
| break; |
| } |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_READ_TIME); |
| r = Esys_ReadClock_Finish((*context)->esys, ¤tTime); |
| return_try_again(r); |
| goto_if_error(r, "ReadClock_Finish.", cleanup_return); |
| |
| (*context)->init_time = *currentTime; |
| SAFE_FREE(currentTime); |
| |
| /* Compute the list of all NULL primary keys stored in keystore. */ |
| r = ifapi_keystore_list_all(&(*context)->keystore, "/HN", &command->pathlist, |
| &command->numPaths); |
| goto_if_error(r, "get entities.", cleanup_return); |
| |
| command->numNullPrimaries = 0; |
| for (i = 0; i < command->numPaths; i++) { |
| if (ifapi_null_primary_p(command->pathlist[i])) { |
| if (i != command->numNullPrimaries) { |
| char *sav_path; |
| sav_path = command->pathlist[command->numNullPrimaries]; |
| command->pathlist[command->numNullPrimaries] = command->pathlist[i]; |
| command->pathlist[i] = sav_path; |
| command->numNullPrimaries += 1; |
| } else { |
| command->numNullPrimaries += 1; |
| } |
| } |
| } |
| command->path_idx = 0; |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_CHECK_NULL_PRIMARY); |
| if (command->path_idx == command->numNullPrimaries) |
| break; |
| |
| r = ifapi_keystore_load_async(&(*context)->keystore, &(*context)->io, |
| command->pathlist[command->path_idx]); |
| goto_if_error2(r, "Could not open %s", cleanup_return, |
| command->pathlist[command->path_idx]); |
| fallthrough; |
| |
| statecase((*context)->state, INITIALIZE_READ_NULL_PRIMARY); |
| r = ifapi_keystore_load_finish(&(*context)->keystore, &(*context)->io, |
| &pkey_object); |
| return_try_again(r); |
| goto_if_error2(r, "Could not open %s", cleanup_return, |
| command->pathlist[command->path_idx]); |
| |
| if (pkey_object.misc.key.reset_count != |
| (*context)->init_time.clockInfo.resetCount) { |
| ifapi_cleanup_ifapi_object(&pkey_object); |
| /* The primary is not valid anymore. */ |
| r = ifapi_keystore_remove_directories(&(*context)->keystore, |
| command->pathlist[command->path_idx]); |
| if (r) { |
| LOG_WARNING("The keys %s in NULL hierarchy cannot be deleted.", |
| command->pathlist[command->path_idx]); |
| } |
| } else { |
| ifapi_cleanup_ifapi_object(&pkey_object); |
| } |
| command->path_idx += 1; |
| (*context)->state = INITIALIZE_CHECK_NULL_PRIMARY; |
| return TSS2_FAPI_RC_TRY_AGAIN; |
| |
| statecasedefault((*context)->state); |
| } |
| |
| (*context)->state = _FAPI_STATE_INIT; |
| SAFE_FREE(*capability); |
| for (size_t i = 0; i < command->numPaths; i++) { |
| SAFE_FREE(command->pathlist[i]); |
| } |
| SAFE_FREE(command->pathlist); |
| LOG_TRACE("finished"); |
| return TSS2_RC_SUCCESS; |
| |
| cleanup_return: |
| /* Cleanup any intermediate results and state stored in the context. */ |
| for (size_t i = 0; i < command->numPaths; i++) { |
| SAFE_FREE(command->pathlist[i]); |
| } |
| SAFE_FREE(command->pathlist); |
| if ((*context)->esys) { |
| Esys_GetTcti((*context)->esys, &fapi_tcti); |
| Esys_Finalize(&(*context)->esys); |
| } |
| if (fapi_tcti) { |
| Tss2_TctiLdr_Finalize(&fapi_tcti); |
| } |
| |
| /* Free the context memory in case of an error. */ |
| free(*context); |
| *context = NULL; |
| |
| return r; |
| } |