| /* 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 <string.h> |
| #include <stdlib.h> |
| |
| #include "tss2_mu.h" |
| #include "fapi_util.h" |
| #include "fapi_crypto.h" |
| //#include "fapi_policy.h" |
| #include "ifapi_helpers.h" |
| #include "ifapi_policy_instantiate.h" |
| #include "ifapi_json_deserialize.h" |
| #include "tpm_json_deserialize.h" |
| #define LOGMODULE fapi |
| #include "util/log.h" |
| #include "util/aux_util.h" |
| |
| static TSS2_RC |
| get_policy_elements(TPML_POLICYELEMENTS *policy, NODE_OBJECT_T **policy_element_list); |
| |
| /** Compute linked list with a list of policy elements which could be instantiated. |
| * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. |
| */ |
| static TSS2_RC |
| get_policy_elements(TPML_POLICYELEMENTS *policy, NODE_OBJECT_T **policy_element_list) |
| { |
| TSS2_RC r = TSS2_RC_SUCCESS; |
| size_t i, j; |
| |
| for (i = 0; i < policy->count; i++) { |
| if (policy->elements[i].type == POLICYOR) { |
| /* Policy with sub policies */ |
| TPML_POLICYBRANCHES *branches = policy->elements[i].element.PolicyOr.branches; |
| for (j = 0; j < branches->count; j++) { |
| r = get_policy_elements(branches->authorizations[j].policy, |
| policy_element_list); |
| goto_if_error(r, "Get policy elements.", error_cleanup); |
| } |
| } else { |
| r = push_object_to_list(&policy->elements[i], policy_element_list); |
| goto_if_error(r, "Get policy elements.", error_cleanup); |
| } |
| } |
| return r; |
| |
| error_cleanup: |
| ifapi_free_node_list(*policy_element_list); |
| return r; |
| } |
| |
| /** Prepare instantiation a policy template. |
| * |
| * Parts of policies which are referenced by object paths will be replaced with |
| * the appropriate values of the referenced objects. |
| * |
| * @param[in] context The context storing information for re-entry after try again. |
| * @param[in] policy The policy to be instantiated. |
| * @param[in] callbacks The needed callback functions with the corresponding user data |
| * which will be passed to the callback. |
| * @retval TSS2_RC_SUCCESS on success. |
| * @retval FAPI error codes on failure |
| * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. |
| */ |
| TSS2_RC |
| ifapi_policyeval_instantiate_async( |
| IFAPI_POLICY_EVAL_INST_CTX *context, /* For re-entry after try_again for offsets and such */ |
| TPMS_POLICY *policy, /* in */ |
| ifapi_policyeval_INST_CB *callbacks) |
| { |
| TSS2_RC r; |
| |
| /* Store callbacks and their parameters in context */ |
| context->callbacks = *callbacks; |
| |
| /* Compute list of all policy elements which have to be instantiated */ |
| if (context->policy_elements) { |
| ifapi_free_object_list(context->policy_elements); |
| context->policy_elements = NULL; |
| } |
| r = get_policy_elements(policy->policy, &context->policy_elements); |
| return r; |
| } |
| |
| /** Compute name and public information format a PEM key. |
| * |
| * @param[in] keyPEM The key in PEM format. |
| * @param[out] keyPublic The public information of the PEM key. |
| * @param[out] name the name computed from the public information. |
| * @param[in] hash_alg The name alg of the key has to passed. |
| * @retval TSS2_RC_SUCCESS on success. |
| */ |
| static TSS2_RC |
| set_pem_key_param( |
| const char *keyPEM, |
| TPMT_PUBLIC *keyPublic, |
| TPM2B_NAME *name, |
| TPMI_ALG_HASH hash_alg) |
| { |
| TSS2_RC r; |
| TPM2B_PUBLIC public; |
| |
| if (!keyPEM || strlen(keyPEM) == 0) { |
| /* No PEM key used. Parameters are already set in policy. */ |
| return TSS2_RC_SUCCESS; |
| } |
| |
| /* Use PEM key to compute public information and name */ |
| name->size = 0; |
| |
| TPM2_ALG_ID rsaOrEcc = ifapi_get_signature_algorithm_from_pem(keyPEM); |
| r = ifapi_initialize_sign_public(rsaOrEcc, &public); |
| return_if_error(r, "Could not initialize public info of key"); |
| |
| r = ifapi_get_tpm2b_public_from_pem(keyPEM, &public); |
| return_if_error(r, "Invalid PEM key."); |
| |
| *keyPublic = public.publicArea; |
| keyPublic->nameAlg = hash_alg; |
| r = ifapi_get_name(&public.publicArea, name); |
| return_if_error(r, "Compute key name."); |
| |
| return TSS2_RC_SUCCESS; |
| } |
| |
| #define CHECK_TEMPLATE_PATH(path, template) \ |
| if (!path) { \ |
| return_error2(TSS2_FAPI_RC_BAD_TEMPLATE, "No path for policy %s", template); \ |
| } |
| |
| /** Finalize instantiation a policy template. |
| * |
| * All needed asyncroous callbacks will be executed for all policy elements offset |
| * The policy. |
| * |
| * @param[in] context The context storing information for re-entry after try again. |
| * @retval TSS2_RC_SUCCESS on success. |
| * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the templayte is not complete for instantiation. |
| * @retval FAPI error codes on failure |
| * @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_REFERENCE a invalid null pointer is passed. |
| * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. |
| * @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_PATH_NOT_FOUND if a FAPI object path was not found |
| * during authorization. |
| * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found. |
| * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the |
| * object store. |
| * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. |
| */ |
| TSS2_RC |
| ifapi_policyeval_instantiate_finish( |
| IFAPI_POLICY_EVAL_INST_CTX *context) |
| { |
| TSS2_RC r = TSS2_RC_SUCCESS; |
| NODE_OBJECT_T *first_in_pol_list = context->policy_elements; |
| size_t i_last; |
| |
| /* While not all policy elements are instantiated */ |
| while (first_in_pol_list) { |
| TPMT_POLICYELEMENT *pol_element = first_in_pol_list->object; |
| switch (pol_element->type) { |
| case POLICYSIGNED: |
| if (pol_element->element.PolicySigned.keyPublic.type) { |
| /* Public info found in template, key path will not be needed. */ |
| SAFE_FREE(pol_element->element.PolicySigned.keyPath); |
| break; |
| } |
| |
| if (pol_element->element.PolicySigned.keyPEM && |
| strlen(pol_element->element.PolicySigned.keyPEM) > 0) { |
| /* Determine name and public info for PEM key. */ |
| r = set_pem_key_param(pol_element->element.PolicySigned.keyPEM, |
| &pol_element->element.PolicySigned.keyPublic, |
| &pol_element->element.PolicySigned.publicKey, |
| pol_element->element.PolicySigned.keyPEMhashAlg); |
| return_if_error(r, "Set parameter of pem key."); |
| |
| /* Clear pem key, will be recreated during execution. */ |
| SAFE_FREE(pol_element->element.PolicySigned.keyPEM); |
| |
| break; |
| } |
| CHECK_TEMPLATE_PATH(pol_element->element.PolicySigned.keyPath, "PolicySigned"); |
| |
| /* Public info will be added to policy. */ |
| r = context->callbacks.cbpublic(pol_element->element.PolicySigned.keyPath, |
| &pol_element->element.PolicySigned.keyPublic, |
| context->callbacks.cbpublic_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| /* Clear keypath, only public data will be needed */ |
| SAFE_FREE(pol_element->element.PolicySigned.keyPath); |
| |
| break; |
| |
| case POLICYNAMEHASH: |
| /* Set index of last name to be computed. */ |
| i_last = pol_element->element.PolicyNameHash.count - 1; |
| while (!pol_element->element.PolicyNameHash.objectNames[i_last].size) { |
| /* Not all object names have been computed or were initialized */ |
| size_t i = pol_element->element.PolicyNameHash.i; |
| r = context->callbacks.cbname(pol_element->element.PolicyNameHash.namePaths[i], |
| &pol_element->element.PolicyNameHash.objectNames[i], |
| context->callbacks.cbname_userdata); |
| return_try_again(r); |
| return_if_error(r, "get object name."); |
| pol_element->element.PolicyNameHash.i++; |
| SAFE_FREE(pol_element->element.PolicyNameHash.namePaths[i]); |
| } |
| break; |
| |
| case POLICYSECRET: |
| if (pol_element->element.PolicySecret.objectName.size) { |
| /* Name found in template, object path will not be needed. */ |
| SAFE_FREE(pol_element->element.PolicySecret.objectPath); |
| break; |
| } |
| CHECK_TEMPLATE_PATH(pol_element->element.PolicySecret.objectPath, "PolicySecret"); |
| /* Object name will be added to policy. */ |
| r = context->callbacks.cbname(pol_element->element.PolicySecret.objectPath, |
| &pol_element->element.PolicySecret.objectName, |
| context->callbacks.cbname_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| SAFE_FREE(pol_element->element.PolicySecret.objectPath); |
| break; |
| |
| case POLICYPCR: |
| if (pol_element->element.PolicyPCR.pcrs && |
| pol_element->element.PolicyPCR.pcrs->count) { |
| /* PCR values already defined */ |
| break; |
| } |
| /* Current values of PCRs will be used for policy */ |
| r = context->callbacks.cbpcr(&pol_element->element.PolicyPCR.currentPCRs, |
| &pol_element->element.PolicyPCR.currentPCRandBanks, |
| &pol_element->element.PolicyPCR.pcrs, |
| context->callbacks.cbpcr_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| |
| pol_element->element.PolicyPCR.currentPCRs.sizeofSelect = 0; |
| pol_element->element.PolicyPCR.currentPCRandBanks.count = 0; |
| break; |
| |
| case POLICYNV: |
| if (pol_element->element.PolicyNV.nvPublic.nvPublic.nvIndex) { |
| /* nvIndex is already set in policy. Path will not be needed */ |
| pol_element->element.PolicyNV.nvIndex |
| = pol_element->element.PolicyNV.nvPublic.nvPublic.nvIndex; |
| SAFE_FREE(pol_element->element.PolicyNV.nvPath); |
| break; |
| } |
| |
| CHECK_TEMPLATE_PATH(pol_element->element.PolicyNV.nvPath, "PolicyNv"); |
| /* Object name will be added to policy. */ |
| r = context->callbacks.cbnvpublic(pol_element->element.PolicyNV.nvPath, |
| &pol_element->element.PolicyNV.nvPublic, |
| context->callbacks.cbnvpublic_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| |
| pol_element->element.PolicyNV.nvIndex |
| = pol_element->element.PolicyNV.nvPublic.nvPublic.nvIndex; |
| |
| /* Clear NV path, only public data will be needed */ |
| SAFE_FREE(pol_element->element.PolicyNV.nvPath); |
| break; |
| |
| case POLICYDUPLICATIONSELECT: |
| if (pol_element->element.PolicyDuplicationSelect.newParentPublic.publicArea.type) { |
| /* public data is already set in policy. Path will not be needed. */ |
| SAFE_FREE(pol_element->element.PolicyDuplicationSelect.newParentPath); |
| break; |
| } |
| |
| CHECK_TEMPLATE_PATH(pol_element->element.PolicyDuplicationSelect.newParentPath, |
| "PolicyDuplicationselect"); |
| |
| /* Public info will be added to policy. */ |
| r = context->callbacks.cbpublic( |
| pol_element->element.PolicyDuplicationSelect.newParentPath, |
| &pol_element->element.PolicyDuplicationSelect.newParentPublic.publicArea, |
| context->callbacks.cbpublic_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| |
| r = ifapi_get_name( |
| &pol_element->element.PolicyDuplicationSelect.newParentPublic.publicArea, |
| &pol_element->element.PolicyDuplicationSelect.newParentName); |
| return_if_error(r, "Compute key name"); |
| |
| /* Clear keypath, only public data will be needed */ |
| SAFE_FREE(pol_element->element.PolicyDuplicationSelect.newParentPath); |
| break; |
| |
| case POLICYAUTHORIZENV: |
| if (pol_element->element.PolicyAuthorizeNv.nvPublic.nvPublic.nvIndex) { |
| /* nvIndex is already set in policy. Path will not be needed */ |
| SAFE_FREE(pol_element->element.PolicyAuthorizeNv.nvPath); |
| break; |
| } |
| |
| CHECK_TEMPLATE_PATH(pol_element->element.PolicyAuthorizeNv.nvPath, |
| "PolicyAuthorizeNv"); |
| /* Object name will be added to policy. */ |
| r = context->callbacks.cbnvpublic(pol_element->element.PolicyAuthorizeNv.nvPath, |
| &pol_element->element.PolicyAuthorizeNv.nvPublic, |
| context->callbacks.cbnvpublic_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| /* Clear NV path, only public data will be needed */ |
| SAFE_FREE(pol_element->element.PolicyAuthorizeNv.nvPath); |
| break; |
| |
| case POLICYAUTHORIZE: |
| if (pol_element->element.PolicyAuthorize.keyPublic.type) { |
| /* Public info found in template, key path will not be needed. */ |
| SAFE_FREE(pol_element->element.PolicyAuthorize.keyPath); |
| r = ifapi_get_name(&pol_element->element.PolicyAuthorize.keyPublic, |
| &pol_element->element.PolicyAuthorize.keyName); |
| return_if_error(r, "Compute key name"); |
| |
| break; |
| } |
| |
| if (pol_element->element.PolicyAuthorize.keyPEM && |
| strlen(pol_element->element.PolicyAuthorize.keyPEM) > 0) { |
| /* Determine name and public info for PEM key. */ |
| r = set_pem_key_param(pol_element->element.PolicyAuthorize.keyPEM, |
| &pol_element->element.PolicyAuthorize.keyPublic, |
| &pol_element->element.PolicyAuthorize.keyName, |
| pol_element->element.PolicyAuthorize.keyPEMhashAlg); |
| return_if_error(r, "Set parameter of pem key."); |
| |
| pol_element->element.PolicyAuthorize.keyPEM = NULL; |
| |
| break; |
| } |
| |
| CHECK_TEMPLATE_PATH(pol_element->element.PolicyAuthorize.keyPath, "PolicyAuthorize"); |
| |
| /* Object public data will be added to policy. */ |
| r = context->callbacks.cbpublic(pol_element->element.PolicyAuthorize.keyPath, |
| &pol_element->element.PolicyAuthorize.keyPublic, |
| context->callbacks.cbpublic_userdata); |
| return_try_again(r); |
| return_if_error(r, "read_finish failed"); |
| |
| /* Compute key name from public info */ |
| r = ifapi_get_name(&pol_element->element.PolicyAuthorize.keyPublic, |
| &pol_element->element.PolicyAuthorize.keyName); |
| return_if_error(r, "Compute key name"); |
| |
| /* Clear key path, only public data will be needed */ |
| SAFE_FREE(pol_element->element.PolicyAuthorize.keyPath); |
| break; |
| } |
| /* Cleanup head of list and use next policy element */ |
| context->policy_elements = first_in_pol_list->next; |
| SAFE_FREE(first_in_pol_list); |
| } |
| return r; |
| } |