| /* 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_policy_execute.h" | 
 | #include "ifapi_policyutil_execute.h" | 
 | #include "ifapi_helpers.h" | 
 | #include "ifapi_json_deserialize.h" | 
 | #include "tpm_json_deserialize.h" | 
 | #include "ifapi_policy_callbacks.h" | 
 | #include "ifapi_policyutil_execute.h" | 
 | #define LOGMODULE fapi | 
 | #include "util/log.h" | 
 | #include "util/aux_util.h" | 
 |  | 
 | /** Create a new policy on policy stack. | 
 |  * | 
 |  * The structures for policy and callback execution are allocated | 
 |  * and the callbacks are assigned. | 
 |  * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. | 
 |  */ | 
 | static TSS2_RC | 
 | new_policy( | 
 |     FAPI_CONTEXT *context, | 
 |     TPMS_POLICY *policy, | 
 |     IFAPI_POLICYUTIL_STACK **current_policy) | 
 | { | 
 |     LOG_DEBUG("ADD POLICY"); | 
 |     IFAPI_POLICY_EXEC_CTX *pol_exec_ctx; | 
 |     IFAPI_POLICY_EXEC_CB_CTX *pol_exec_cb_ctx; | 
 |  | 
 |     *current_policy = calloc(sizeof(IFAPI_POLICYUTIL_STACK), 1); | 
 |     if (!*current_policy) { | 
 |         return_error(TSS2_FAPI_RC_MEMORY, "Out of memory"); | 
 |     } | 
 |  | 
 |     pol_exec_ctx = calloc(sizeof(IFAPI_POLICY_EXEC_CTX), 1); | 
 |     if (!pol_exec_ctx) { | 
 |         return_error(TSS2_FAPI_RC_MEMORY, "Out of memory"); | 
 |     } | 
 |     (*current_policy)->pol_exec_ctx = pol_exec_ctx; | 
 |     pol_exec_ctx->callbacks.cbauth = ifapi_policyeval_cbauth; | 
 |     pol_exec_ctx->callbacks.cbauth_userdata = context; | 
 |     pol_exec_ctx->callbacks.cbpolsel = ifapi_branch_selection; | 
 |     pol_exec_ctx->callbacks.cbpolsel_userdata = context; | 
 |     pol_exec_ctx->callbacks.cbsign = ifapi_sign_buffer; | 
 |     pol_exec_ctx->callbacks.cbsign_userdata = context; | 
 |     pol_exec_ctx->callbacks.cbauthpol = ifapi_exec_auth_policy; | 
 |     pol_exec_ctx->callbacks.cbauthpol_userdata = context; | 
 |     pol_exec_ctx->callbacks.cbauthnv = ifapi_exec_auth_nv_policy; | 
 |     pol_exec_ctx->callbacks.cbauthnv_userdata = context; | 
 |     pol_exec_ctx->callbacks.cbdup = ifapi_get_duplicate_name; | 
 |     pol_exec_ctx->callbacks.cbdup_userdata = context; | 
 |     pol_exec_ctx->callbacks.cbaction = ifapi_policy_action; | 
 |     pol_exec_ctx->callbacks.cbaction_userdata = context; | 
 |  | 
 |     pol_exec_cb_ctx = calloc(sizeof(IFAPI_POLICY_EXEC_CB_CTX), 1); | 
 |     if (!pol_exec_cb_ctx) { | 
 |         return_error(TSS2_FAPI_RC_MEMORY, "Out of memory"); | 
 |     } | 
 |     pol_exec_ctx->app_data = pol_exec_cb_ctx; | 
 |     pol_exec_ctx->policy = policy; | 
 |     if (!context->policy.policyutil_stack) { | 
 |         context->policy.policyutil_stack = *current_policy; | 
 |         context->policy.util_current_policy = *current_policy; | 
 |     } else { | 
 |         context->policy.util_current_policy->next = *current_policy; | 
 |         (*current_policy)->prev = context->policy.util_current_policy; | 
 |     } | 
 |     return TSS2_RC_SUCCESS; | 
 | } | 
 |  | 
 | /** Compute a new session which will be uses as policy session. | 
 |  * @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_GENERAL_FAILURE if an internal error occurred. | 
 |  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. | 
 |  */ | 
 | static TSS2_RC | 
 | create_session( | 
 |     FAPI_CONTEXT *context, | 
 |     ESYS_TR *session, | 
 |     TPMI_ALG_HASH hash_alg) | 
 | { | 
 |     TSS2_RC r = TSS2_RC_SUCCESS; | 
 |  | 
 |     switch (context->policy.create_session_state) { | 
 |     case CREATE_SESSION_INIT: | 
 |         r = Esys_StartAuthSession_Async(context->esys, | 
 |                                         context->srk_handle ? context->srk_handle : ESYS_TR_NONE, | 
 |                                         ESYS_TR_NONE, | 
 |                                         ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, | 
 |                                         NULL, | 
 |                                         TPM2_SE_POLICY, | 
 |                                         &context->profiles.default_profile.session_symmetric, | 
 |                                         hash_alg); | 
 |  | 
 |         return_if_error(r, "Creating session."); | 
 |  | 
 |         context->policy.create_session_state = WAIT_FOR_CREATE_SESSION; | 
 |         return TSS2_FAPI_RC_TRY_AGAIN; | 
 |  | 
 |     case WAIT_FOR_CREATE_SESSION: | 
 |         r = Esys_StartAuthSession_Finish(context->esys, session); | 
 |         if (r != TSS2_RC_SUCCESS) | 
 |             return r; | 
 |         context->policy.create_session_state = CREATE_SESSION_INIT; | 
 |         break; | 
 |  | 
 |     default: | 
 |         context->state = _FAPI_STATE_INTERNALERROR; | 
 |         goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid state for create session.", | 
 |                    cleanup); | 
 |     } | 
 |  | 
 | cleanup: | 
 |     return r; | 
 | } | 
 |  | 
 | /** Cleanup the current policy and adapt the policy stack. | 
 |  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. | 
 |  */ | 
 | static TSS2_RC | 
 | clear_current_policy(FAPI_CONTEXT *context) | 
 | { | 
 |     LOG_DEBUG("CLEAR POLICY"); | 
 |     IFAPI_POLICYUTIL_STACK *prev_pol; | 
 |     if (!context->policy.util_current_policy) { | 
 |         return_error(TSS2_FAPI_RC_GENERAL_FAILURE, "No current policy."); | 
 |     } | 
 |     prev_pol = context->policy.util_current_policy->prev; | 
 |  | 
 |     SAFE_FREE(context->policy.util_current_policy->pol_exec_ctx->app_data); | 
 |     SAFE_FREE(context->policy.util_current_policy->pol_exec_ctx); | 
 |     SAFE_FREE(context->policy.util_current_policy); | 
 |  | 
 |     if (!prev_pol) { | 
 |         context->policy.policyutil_stack = NULL; | 
 |     } else { | 
 |         prev_pol->next = NULL; | 
 |     } | 
 |     return TSS2_RC_SUCCESS; | 
 | } | 
 |  | 
 | /** Cleanup the policy stack. | 
 |   * | 
 |   * Will be used if an error occurs. | 
 |   */ | 
 | static void | 
 | clear_all_policies(FAPI_CONTEXT *context) | 
 | { | 
 |     LOG_DEBUG("CLEAR ALL POLICIES"); | 
 |  | 
 |     IFAPI_POLICYUTIL_STACK *policy = context->policy.policyutil_stack; | 
 |     IFAPI_POLICYUTIL_STACK *next_policy; | 
 |  | 
 |     while (policy) { | 
 |         next_policy = policy->next; | 
 |         SAFE_FREE(policy->pol_exec_ctx->app_data); | 
 |         if (policy->pol_exec_ctx->session) | 
 |             Esys_FlushContext(context->esys, policy->pol_exec_ctx->session); | 
 |         SAFE_FREE(policy->pol_exec_ctx); | 
 |         ; | 
 |         SAFE_FREE(policy); | 
 |         policy = next_policy; | 
 |     } | 
 |     context->policy.policyutil_stack = NULL; | 
 | } | 
 |  | 
 | /** Prepare the execution of a new policy on policy stack. | 
 |  * | 
 |  * The context for the  policy utility, the policy execution and the needed | 
 |  * callbacks is initialized. | 
 |  * The policy execution will be prepared. In this step the list of policies | 
 |  * to be executed will be computed. | 
 |  * @param[in,out] context The fapi context with the pointer to the policy stack. | 
 |  * @param[in] hash_alg The hash algorithm used for the policy computation. | 
 |  * @param[in,out] policy The policy to be executed. Some policy elements will | 
 |  *                be used to store computed parameters needed for policy | 
 |  *                execution. | 
 |  * @retval TSS2_RC_SUCCESS on success. | 
 |  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for branch selection is | 
 |  *         not defined. This callback will be needed of or policies have to be | 
 |  *         executed. | 
 |  * @retval TSS2_FAPI_RC_BAD_VALUE If the computed branch index deliverd by the | 
 |  *         callback does not identify a branch. | 
 |  * @retval TSS2_FAPI_RC_BAD_REFERENCE If no context is passed. | 
 |  * | 
 |  * @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated. | 
 |  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails. | 
 |  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. | 
 |  */ | 
 | TSS2_RC | 
 | ifapi_policyutil_execute_prepare( | 
 |     FAPI_CONTEXT *context, | 
 |     TPMI_ALG_HASH hash_alg, | 
 |     TPMS_POLICY *policy) | 
 | { | 
 |     TSS2_RC r; | 
 |     IFAPI_POLICYUTIL_STACK *current_policy; | 
 |  | 
 |     return_if_null(context, "Bad context.", TSS2_FAPI_RC_BAD_REFERENCE); | 
 |  | 
 |     r = new_policy(context, policy, ¤t_policy); | 
 |     goto_if_error(r, "Create new policy.", error); | 
 |  | 
 |     r = ifapi_policyeval_execute_prepare(current_policy->pol_exec_ctx, hash_alg, policy); | 
 |     goto_if_error(r, "Prepare policy execution.", error); | 
 |  | 
 |     return r; | 
 |  | 
 | error: | 
 |     while (context->policy.policyutil_stack) { | 
 |         clear_all_policies(context); | 
 |     } | 
 |     SAFE_FREE(current_policy); | 
 |     return r; | 
 | } | 
 | /** State machine to Execute the TPM policy commands needed for the current policy. | 
 |  * | 
 |  * In the first step a session will be created if no session is passed. | 
 |  * In the second step the policy engine will execute the policy. | 
 |  * | 
 |  * @param[in,out] context The fapi context with the pointer to the policy stack. | 
 |  * @param[in,out] session The policy session to be extended or if the value is | 
 |  *                equal zero or ESYS_TR_NONE a new created session will been | 
 |  *                be stored in this parameter. | 
 |  * @retval TSS2_RC_SUCCESS on success. | 
 |  * @retval TSS2_FAPI_RC_MEMORY: if not enough memory can be allocated. | 
 |  * @retval TSS2_FAPI_RC_BAD_VALUE If wrong values are detected during execution. | 
 |  * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy | 
 |  *         store. | 
 |  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was | 
 |  *         not successful. | 
 |  * @retval TSS2_FAPI_RC_BAD_TEMPLATE In a invalid policy is loaded during execution. | 
 |  * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy | 
 |  *         execution fails. | 
 |  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. | 
 |  * @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_BAD_REFERENCE a invalid null pointer is passed. | 
 |  * @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_AUTHORIZATION_UNKNOWN if a required authorization callback | 
 |  *         is not set. | 
 |  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails. | 
 |  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. | 
 |  */ | 
 | TSS2_RC | 
 | ifapi_policyutil_execute(FAPI_CONTEXT *context, ESYS_TR *session) | 
 | { | 
 |     TSS2_RC r; | 
 |     IFAPI_POLICYUTIL_STACK *pol_util_ctx; | 
 |     TPMI_ALG_HASH hash_alg; | 
 |  | 
 |     if (context->policy.util_current_policy) { | 
 |         pol_util_ctx = context->policy.util_current_policy->next; | 
 |         context->policy.util_current_policy = context->policy.util_current_policy->next; | 
 |     } else { | 
 |         pol_util_ctx = context->policy.policyutil_stack; | 
 |         context->policy.util_current_policy = pol_util_ctx; | 
 |     } | 
 |     LOG_TRACE("Util context: %p", pol_util_ctx); | 
 |  | 
 |     if (!pol_util_ctx) { | 
 |         return_error(TSS2_FAPI_RC_GENERAL_FAILURE, "No policy util stack."); | 
 |     } | 
 |  | 
 |     switch (pol_util_ctx->state) { | 
 |         statecase(pol_util_ctx->state, POLICY_UTIL_INIT); | 
 |             LOG_DEBUG("Util session: %x", pol_util_ctx->policy_session); | 
 |             if (*session == ESYS_TR_NONE  || *session == 0) { | 
 |                 /* Create a new  policy session for the current policy execution */ | 
 |                 hash_alg = pol_util_ctx->pol_exec_ctx->hash_alg; | 
 |                 r = create_session(context, &pol_util_ctx->policy_session, | 
 |                                   hash_alg); | 
 |                 if ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN) { | 
 |                     context->policy.util_current_policy = pol_util_ctx->prev; | 
 |                     return TSS2_FAPI_RC_TRY_AGAIN; | 
 |                 } | 
 |                 goto_if_error(r, "Create policy session", error); | 
 |  | 
 |                 pol_util_ctx->pol_exec_ctx->session = pol_util_ctx->policy_session; | 
 |             } else { | 
 |                 pol_util_ctx->pol_exec_ctx->session = *session; | 
 |             } | 
 |             fallthrough; | 
 |  | 
 |         statecase(pol_util_ctx->state, POLICY_UTIL_EXEC_POLICY); | 
 |             r = ifapi_policyeval_execute(context->esys, | 
 |                                          pol_util_ctx->pol_exec_ctx); | 
 |             if ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN) { | 
 |                 context->policy.util_current_policy = pol_util_ctx->prev; | 
 |                 return TSS2_FAPI_RC_TRY_AGAIN; | 
 |             } | 
 |             goto_if_error(r, "Execute policy.", error); | 
 |  | 
 |             break; | 
 |  | 
 |         statecasedefault(pol_util_ctx->state); | 
 |     } | 
 |     *session = pol_util_ctx->policy_session; | 
 |  | 
 |     pol_util_ctx = pol_util_ctx->prev; | 
 |  | 
 |     r = clear_current_policy(context); | 
 |     goto_if_error(r, "Clear policy.", error); | 
 |  | 
 |     context->policy.util_current_policy = pol_util_ctx; | 
 |  | 
 |     LOG_TRACE("success"); | 
 |     return r; | 
 |  | 
 | error: | 
 |     while (context->policy.policyutil_stack) { | 
 |         clear_all_policies(context); | 
 |     } | 
 |     return r; | 
 | } |