blob: acbcfedaca78cb0241bf67122526ebda60bbc750 [file] [log] [blame]
/* 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, &currentTime);
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;
}