blob: 09580d2c873346935f2dbf8dcd4515d7c48c7e59 [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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "tss2_common.h"
#include "ifapi_profiles.h"
#include "tpm_json_deserialize.h"
#include "ifapi_policy_json_deserialize.h"
#include "ifapi_json_deserialize.h"
#include "ifapi_helpers.h"
#define LOGMODULE fapi
#include "util/log.h"
#include "util/aux_util.h"
#include "ifapi_macros.h"
#define PROFILES_EXTENSION ".json"
#define PROFILES_PREFIX "P_"
static TSS2_RC
ifapi_profile_json_deserialize(
json_object *jso,
IFAPI_PROFILE *profile);
static TSS2_RC
ifapi_profile_checkpcrs(const TPML_PCR_SELECTION *pcr_profile);
/** Initialize the profiles information in the context in an asynchronous way
*
* Load the profile information from disk, fill the dictionary of loaded profiles and fill
* the default profile information into the context.
*
* Call ifapi_profiles_initialize_finish to complete the operation.
*
* @param[in,out] profiles The context for the profiles information.
* @param[in,out] io The input/output context being used for file I/O.
* @param[in] profilesdir The directory to load profile information from.
* @param[in] defaultprofile The name of the default profile to use.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_REFERENCE if NULL pointers were passed in.
* @retval TSS2_FAPI_RC_BAD_VALUE if the profilesdir does not exist or is empty.
* @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.
*/
TSS2_RC
ifapi_profiles_initialize_async(
IFAPI_PROFILES *profiles,
IFAPI_IO *io,
const char *profilesdir,
const char *defaultprofile)
{
TSS2_RC r;
char *tmp;
size_t i, j;
check_not_null(profiles);
check_not_null(profilesdir);
memset(profiles, 0, sizeof(*profiles));
profiles->default_name = strdup(defaultprofile);
check_oom(profiles->default_name);
r = ifapi_io_dirfiles(profilesdir, &profiles->filenames, &profiles->num_profiles);
return_if_error(r, "Reading profiles from profiles dir");
profiles->profiles = calloc(profiles->num_profiles, sizeof(profiles->profiles[0]));
check_oom(profiles->profiles);
/* Clean up list of files to only include those that match our filename pattern
Expand the filenames with the directory
Set the names in the dictionary */
for (i = 0; i < profiles->num_profiles; ) {
char *ext = strstr(profiles->filenames[i], PROFILES_EXTENSION);
/* Path the filename with the expected pattern */
if (ext != NULL && strlen(ext) == strlen(PROFILES_EXTENSION) &&
strncmp(profiles->filenames[i], PROFILES_PREFIX, strlen(PROFILES_PREFIX)) == 0)
{
LOG_TRACE("Using file %s in profiles directory", profiles->filenames[i]);
/* Add the profile name */
profiles->profiles[i].name = strndup(profiles->filenames[i],
ext - profiles->filenames[i]);
check_oom(profiles->profiles[i].name);
/* Expand the filename with the directory */
tmp = profiles->filenames[i];
r = ifapi_asprintf(&profiles->filenames[i], "%s/%s", profilesdir, tmp);
return_if_error(r, "Out of memory");
LOG_TRACE("Added profile-entry %s for file %s based on direntry %s",
profiles->profiles[i].name, profiles->filenames[i], tmp);
/* Cleanup and continue */
free(tmp);
i++;
} else {
LOG_TRACE("Skipping file %s in profiles directory", profiles->filenames[i]);
free(profiles->filenames[i]);
profiles->num_profiles -= 1;
for (j = i; j < profiles->num_profiles; j++) {
profiles->filenames[j] = profiles->filenames[j + 1];
}
}
}
if (profiles->num_profiles == 0) {
LOG_ERROR("No files found in profile dir %s that match the pattern %s*%s",
profilesdir, PROFILES_PREFIX, PROFILES_EXTENSION);
return TSS2_FAPI_RC_BAD_VALUE;
}
#ifdef HAVE_REALLOCARRAY
profiles->profiles = reallocarray(profiles->profiles, profiles->num_profiles,
sizeof(profiles->profiles[0]));
profiles->filenames = reallocarray(profiles->filenames, profiles->num_profiles,
sizeof(profiles->filenames[0]));
#else /* HAVE_REALLOCARRAY */
profiles->profiles = realloc(profiles->profiles, profiles->num_profiles *
sizeof(profiles->profiles[0]));
profiles->filenames = realloc(profiles->filenames, profiles->num_profiles *
sizeof(profiles->filenames[0]));
#endif /* HAVE_REALLOCARRAY */
/* No need for OOM checks, since num_profiles may only have become smaller */
r = ifapi_io_read_async(io, profiles->filenames[profiles->profiles_idx]);
return_if_error2(r, "Reading profile %s", profiles->filenames[profiles->profiles_idx]);
return TSS2_RC_SUCCESS;
}
/** Initialize the profiles information in the context in an asynchronous way
*
* Call after ifapi_profiles_initialize_async to complete the operation.
*
* @param[in,out] profiles The context for the profiles information.
* @param[in,out] io The input/output context being used for file I/O.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_REFERENCE if NULL pointers were passed in.
* @retval TSS2_FAPI_RC_BAD_VALUE if a profile could not be loaded.
* @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.
*/
TSS2_RC
ifapi_profiles_initialize_finish(
IFAPI_PROFILES *profiles,
IFAPI_IO *io)
{
TSS2_RC r;
uint8_t *buffer;
size_t i;
json_object *jso;
check_not_null(profiles);
check_not_null(io);
r = ifapi_io_read_finish(io, &buffer, NULL);
return_if_error(r, "Reading profile failed");
jso = json_tokener_parse((char *) buffer);
free(buffer);
if (jso == NULL) {
LOG_ERROR("Failed to parse profile %s", profiles->filenames[profiles->profiles_idx]);
r = TSS2_FAPI_RC_BAD_VALUE;
goto error;
}
r = ifapi_profile_json_deserialize(jso,
&profiles->profiles[profiles->profiles_idx].profile);
json_object_put(jso);
goto_if_error2(r, "Parsing profile %s failed", error,
profiles->filenames[profiles->profiles_idx]);
r = ifapi_profile_checkpcrs(&profiles->profiles[profiles->profiles_idx].profile.pcr_selection);
goto_if_error2(r, "Malformed profile pcr selection for profile %s", error,
profiles->filenames[profiles->profiles_idx]);
profiles->profiles_idx += 1;
if (profiles->profiles_idx < profiles->num_profiles) {
r = ifapi_io_read_async(io, profiles->filenames[profiles->profiles_idx]);
goto_if_error2(r, "Reading profile %s", error,
profiles->filenames[profiles->profiles_idx]);
return TSS2_FAPI_RC_TRY_AGAIN;
}
/* Get the data of the default profile into the respective variable */
for (i = 0; i < profiles->num_profiles; i++) {
if (strcmp(profiles->default_name, profiles->profiles[i].name) == 0) {
profiles->default_profile = profiles->profiles[i].profile;
break;
}
}
if (i == profiles->num_profiles) {
LOG_ERROR("Default profile %s not in the list of loaded profiles",
profiles->default_name);
r = TSS2_FAPI_RC_BAD_VALUE;
goto error;
}
for (i = 0; i < profiles->num_profiles; i++) {
free(profiles->filenames[i]);
}
SAFE_FREE(profiles->filenames);
return TSS2_RC_SUCCESS;
error:
for (i = 0; i < profiles->num_profiles; i++) {
SAFE_FREE(profiles->filenames[i]);
}
SAFE_FREE(profiles->filenames);
ifapi_profiles_finalize(profiles);
return r;
}
/** Return the profile data for a given profile name.
*
* Returns a (const, not to be free'd) pointer to the profile data for a requested profile.
* If a NULL profile is requesten, then the default profile is returned.
* If a keypath is passed in, then the prefix is analysed. If that keypath starts with a profile
* then this profile is returned. Otherwise the default profile is returned.
*
* @param[in] profiles The profiles context
* @param[in] name The name of the profile or the keypath
* @param[out] profile The pointer to the profile data.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_REFERENCE if NULL pointers were passed in.
* @retval TSS2_FAPI_RC_BAD_VALUE if a profile is not found.
*/
TSS2_RC
ifapi_profiles_get(
const IFAPI_PROFILES *profiles,
const char *name,
const IFAPI_PROFILE **profile)
{
check_not_null(profiles);
check_not_null(name);
check_not_null(profile);
char *split;
size_t len;
/* if no name or nor profile prefix is given, use the default profile */
if (!name || strncmp(name, "P_", 2) != 0 || strncmp(name, "/P_", 2) != 0) {
*profile = &profiles->default_profile;
return TSS2_RC_SUCCESS;
}
/* Search for path delimiter */
split = index(name, IFAPI_FILE_DELIM_CHAR);
/* If the path beging with delimiters, skip over those */
if (name == split) {
name += 1;
split = index(name, IFAPI_FILE_DELIM_CHAR);
}
if (split == NULL)
len = strlen(name);
else
len = split - name;
for (size_t i = 0; i < profiles->num_profiles; i++) {
if (len == strlen(profiles->profiles[i].name) &&
strncmp(name, profiles->profiles[i].name, len) == 0) {
*profile = &profiles->profiles[i].profile;
return TSS2_RC_SUCCESS;
}
}
LOG_ERROR("Profile %s not in the list of loaded profiles", name);
return TSS2_FAPI_RC_BAD_VALUE;
}
/** Sanitizes and frees internal data structures of loaded profiles' information.
*
* @param[in,out] profiles The context for the profiles information.
*/
void
ifapi_profiles_finalize(
IFAPI_PROFILES *profiles)
{
size_t i;
if (!profiles) {
LOG_ERROR("Called with bad reference");
return;
}
SAFE_FREE(profiles->default_name);
for (i = 0; i < profiles->num_profiles; i++) {
IFAPI_PROFILE_ENTRY * entry = &profiles->profiles[i];
SAFE_FREE(profiles->profiles[i].name);
IFAPI_PROFILE * profile = &entry->profile;
SAFE_FREE(profile->srk_template);
SAFE_FREE(profile->ek_template);
SAFE_FREE(profile->srk_description);
SAFE_FREE(profile->ek_description);
ifapi_cleanup_policy(profile->eh_policy);
SAFE_FREE(profile->eh_policy);
ifapi_cleanup_policy(profile->ek_policy);
SAFE_FREE(profile->ek_policy);
ifapi_cleanup_policy(profile->sh_policy);
SAFE_FREE(profile->sh_policy);
}
SAFE_FREE(profiles->profiles);
memset(profiles, 0, sizeof(*profiles));
}
/** Deserialize a IFAPI_KEY_PROFILE 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_IO_ERROR if an error occurred while accessing the
* object store.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
*/
static TSS2_RC
ifapi_profile_json_deserialize(
json_object *jso,
IFAPI_PROFILE *out)
{
json_object *jso2;
TSS2_RC r;
const TPMT_SYM_DEF session_symmetric_default = {
.algorithm = TPM2_ALG_AES,
.keyBits = {.aes = 128},
.mode = {.aes = TPM2_ALG_CFB}
};
LOG_TRACE("call");
memset(out, 0, sizeof(IFAPI_PROFILE));
return_if_null(out, "Bad reference.", TSS2_FAPI_RC_BAD_REFERENCE);
if (!ifapi_get_sub_object(jso, "type", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPMI_ALG_PUBLIC_deserialize(jso2, &out->type);
return_if_error(r, "BAD VALUE");
if (!ifapi_get_sub_object(jso, "srk_template", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
out->srk_template = strdup(json_object_get_string(jso2));
return_if_null(out->srk_template, "Out of memory.", TSS2_FAPI_RC_MEMORY);
if (!ifapi_get_sub_object(jso, "srk_description", &jso2)) {
out->srk_description = NULL;
} else {
out->srk_description = strdup(json_object_get_string(jso2));
return_if_null(out->srk_description, "Out of memory.", TSS2_FAPI_RC_MEMORY);
}
if (!ifapi_get_sub_object(jso, "ek_template", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
out->ek_template = strdup(json_object_get_string(jso2));
return_if_null(out->ek_template, "Out of memory.", TSS2_FAPI_RC_MEMORY);
if (!ifapi_get_sub_object(jso, "ek_description", &jso2)) {
out->ek_description = NULL;
} else {
out->ek_description = strdup(json_object_get_string(jso2));
return_if_null(out->ek_description, "Out of memory.", TSS2_FAPI_RC_MEMORY);
}
if (!ifapi_get_sub_object(jso, "ecc_signing_scheme", &jso2)) {
memset(&out->ecc_signing_scheme, 0, sizeof(TPMT_SIG_SCHEME));
} else {
r = ifapi_json_TPMT_SIG_SCHEME_deserialize(jso2, &out->ecc_signing_scheme);
return_if_error(r, "BAD VALUE");
}
if (!ifapi_get_sub_object(jso, "rsa_signing_scheme", &jso2)) {
memset(&out->rsa_signing_scheme, 0, sizeof(TPMT_SIG_SCHEME));
} else {
r = ifapi_json_TPMT_SIG_SCHEME_deserialize(jso2, &out->rsa_signing_scheme);
return_if_error(r, "BAD VALUE");
}
if (!ifapi_get_sub_object(jso, "rsa_decrypt_scheme", &jso2)) {
memset(&out->rsa_decrypt_scheme, 0, sizeof(TPMT_RSA_DECRYPT));
} else {
r = ifapi_json_TPMT_RSA_DECRYPT_deserialize(jso2, &out->rsa_decrypt_scheme);
return_if_error(r, "BAD VALUE");
}
if (!ifapi_get_sub_object(jso, "sym_mode", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPMI_ALG_SYM_MODE_deserialize(jso2, &out->sym_mode);
return_if_error(r, "BAD VALUE");
if (!ifapi_get_sub_object(jso, "sym_parameters", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPMT_SYM_DEF_OBJECT_deserialize(jso2, &out->sym_parameters);
return_if_error(r, "BAD VALUE");
if (!ifapi_get_sub_object(jso, "sym_block_size", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_UINT16_deserialize(jso2, &out->sym_block_size);
return_if_error(r, "BAD VALUE");
if (!ifapi_get_sub_object(jso, "pcr_selection", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPML_PCR_SELECTION_deserialize(jso2, &out->pcr_selection);
return_if_error(r, "BAD VALUE");
if (!ifapi_get_sub_object(jso, "nameAlg", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPMI_ALG_HASH_deserialize(jso2, &out->nameAlg);
return_if_error(r, "BAD VALUE");
if (out->type == TPM2_ALG_RSA) {
if (!ifapi_get_sub_object(jso, "exponent", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_UINT32_deserialize(jso2, &out->exponent);
return_if_error(r, "BAD VALUE");
if (!ifapi_get_sub_object(jso, "keyBits", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPMI_RSA_KEY_BITS_deserialize(jso2, &out->keyBits);
return_if_error(r, "BAD VALUE");
} else if (out->type == TPM2_ALG_ECC) {
if (!ifapi_get_sub_object(jso, "curveID", &jso2)) {
LOG_ERROR("Bad value");
return TSS2_FAPI_RC_BAD_VALUE;
}
r = ifapi_json_TPMI_ECC_CURVE_deserialize(jso2, &out->curveID);
return_if_error(r, "BAD VALUE");
}
if (!ifapi_get_sub_object(jso, "session_symmetric", &jso2)) {
out->session_symmetric = session_symmetric_default;
} else {
r = ifapi_json_TPMT_SYM_DEF_deserialize(jso2, &out->session_symmetric);
return_if_error(r, "BAD VALUE");
}
if (ifapi_get_sub_object(jso, "eh_policy", &jso2)) {
out->eh_policy = calloc(1, sizeof(TPMS_POLICY));
goto_if_null2(out->eh_policy, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
cleanup);
r = ifapi_json_TPMS_POLICY_deserialize(jso2, out->eh_policy);
goto_if_error(r, "Deserialize policy.", cleanup);
}
if (ifapi_get_sub_object(jso, "sh_policy", &jso2)) {
out->sh_policy = calloc(1, sizeof(TPMS_POLICY));
goto_if_null2(out->sh_policy, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
cleanup);
r = ifapi_json_TPMS_POLICY_deserialize(jso2, out->sh_policy);
goto_if_error(r, "Deserialize policy.", cleanup);
}
if (ifapi_get_sub_object(jso, "ek_policy", &jso2)) {
out->ek_policy = calloc(1, sizeof(TPMS_POLICY));
goto_if_null2(out->ek_policy, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
cleanup);
r = ifapi_json_TPMS_POLICY_deserialize(jso2, out->ek_policy);
goto_if_error(r, "Deserialize policy.", cleanup);
}
if (ifapi_get_sub_object(jso, "srk_policy", &jso2)) {
out->srk_policy = calloc(1, sizeof(TPMS_POLICY));
goto_if_null2(out->srk_policy, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
cleanup);
r = ifapi_json_TPMS_POLICY_deserialize(jso2, out->srk_policy);
goto_if_error(r, "Deserialize policy.", cleanup);
}
if (ifapi_get_sub_object(jso, "lockout_policy", &jso2)) {
out->lockout_policy = calloc(1, sizeof(TPMS_POLICY));
goto_if_null2(out->lockout_policy, "Out of memory.", r, TSS2_FAPI_RC_MEMORY,
cleanup);
r = ifapi_json_TPMS_POLICY_deserialize(jso2, out->lockout_policy);
goto_if_error(r, "Deserialize policy.", cleanup);
}
if (!ifapi_get_sub_object(jso, "newMaxTries", &jso2)) {
out->newMaxTries = 5;
} else {
r = ifapi_json_UINT32_deserialize(jso2, &out->newMaxTries);
return_if_error(r, "BAD VALUE");
}
if (!ifapi_get_sub_object(jso, "newRecoveryTime", &jso2)) {
out->newRecoveryTime = 1000;
} else {
r = ifapi_json_UINT32_deserialize(jso2, &out->newRecoveryTime);
return_if_error(r, "BAD VALUE");
}
if (!ifapi_get_sub_object(jso, "lockoutRecovery", &jso2)) {
out->lockoutRecovery = 1000;
} else {
r = ifapi_json_UINT32_deserialize(jso2, &out->lockoutRecovery);
return_if_error(r, "BAD VALUE");
}
LOG_TRACE("true");
return TSS2_RC_SUCCESS;
cleanup:
SAFE_FREE(out->eh_policy);
return r;
}
/**
* Check whether PCRs with muliple banks are defined in profile.
*
* This case is not allowed by FAPI.
*/
static TSS2_RC
ifapi_profile_checkpcrs(const TPML_PCR_SELECTION *pcr_profile)
{
size_t i, j, byte_idx;
for (i = 0; i < pcr_profile->count - 1; i++) {
for (j = i + 1; j < pcr_profile->count; j++) {
for (byte_idx = 0; byte_idx < 3; byte_idx++) {
/* Check whether a PCR register flag does occur in two different banks. */
if (pcr_profile->pcrSelections[i].pcrSelect[byte_idx] &
pcr_profile->pcrSelections[j].pcrSelect[byte_idx]) {
return_error2(TSS2_FAPI_RC_BAD_VALUE,
"More than one bank selected for a PCR register.");
}
}
}
}
return TSS2_RC_SUCCESS;
}