/* SPDX-License-Identifier: BSD-2-Clause */ /******************************************************************************* * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG * All rights reserved. *******************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "fapi_util.h" #include "fapi_policy.h" #include "ifapi_helpers.h" #include "fapi_crypto.h" #include "ifapi_policy_instantiate.h" #include "ifapi_policyutil_execute.h" #include "ifapi_policy_execute.h" #include "ifapi_policy_callbacks.h" #include "tss2_mu.h" #define LOGMODULE fapi #include "util/log.h" #include "util/aux_util.h" /** Determine the auth object of a NV index. * * The auth object is determined depending on the object flags. * * @param[in] nv_object The internal FAPI object representing the NV index. * @param[out] nv_index The ESYS handle of the NV index. * @param[out] auth_object The internal FAPI auth object. * @param[out] auth_index The ESYS handle of the auth object. * @retval TSS2_RC_SUCCESS on success. */ static void get_nv_auth_object( IFAPI_OBJECT *nv_object, ESYS_TR nv_index, IFAPI_OBJECT *auth_object, ESYS_TR *auth_index) { if (nv_object->misc.nv.public.nvPublic.attributes & TPMA_NV_PPREAD) { ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_PLATFORM); *auth_index = ESYS_TR_RH_PLATFORM; } else { if (nv_object->misc.nv.public.nvPublic.attributes & TPMA_NV_OWNERREAD) { ifapi_init_hierarchy_object(auth_object, ESYS_TR_RH_OWNER); *auth_index = ESYS_TR_RH_OWNER; } else { *auth_index = nv_index; *auth_object = *nv_object; } } } /** Get public data of a key from keystore. * * @param[in] path The relative path of the key. * @param[out] public The caller allocated public structure. * @param[in,out] ctx The context to access io and keystore module and to store * the io state. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be loaded. * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for * internal operations or return parameters. * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the loaded template is not * appropriate for this operation. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @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_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_GENERAL_FAILURE if an internal error occurred. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_get_key_public( const char *path, TPMT_PUBLIC *public, void *ctx) { TSS2_RC r = TSS2_RC_SUCCESS; IFAPI_OBJECT object; FAPI_CONTEXT *context = ctx; switch (context->io_state) { statecase(context->io_state, IO_INIT) /* Prepare the loading of the object. */ r = ifapi_keystore_load_async(&context->keystore, &context->io, path); return_if_error2(r, "Could not open: %s", path); fallthrough; statecase(context->io_state, IO_ACTIVE) /* Finalize or retry the reading and check the object type */ r = ifapi_keystore_load_finish(&context->keystore, &context->io, &object); return_try_again(r); return_if_error(r, "read_finish failed"); switch (object.objectType) { case IFAPI_KEY_OBJ: *public = object.misc.key.public.publicArea; break; case IFAPI_EXT_PUB_KEY_OBJ: *public = object.misc.ext_pub_key.public.publicArea; break; default: goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Object %s is not a key.", cleanup, path); } break; statecasedefault_error(context->state, r, cleanup); } cleanup: context->io_state = IO_INIT; ifapi_cleanup_ifapi_object(&object); return r; } /** Get TPM name of an object from key keystore. * * @param[in] path The relative path of the object. * @param[out] name The caller allocate public structure. * @param[in,out] ctx The context to access io and keystore module and to store * the io state. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be loaded. * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for * internal operations or return parameters. * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the loaded template is not * appropriate for this operation. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @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_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_GENERAL_FAILURE if an internal error occurred. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_get_object_name( const char *path, TPM2B_NAME *name, void *ctx) { TSS2_RC r = TSS2_RC_SUCCESS; IFAPI_OBJECT object; FAPI_CONTEXT *context = ctx; switch (context->io_state) { statecase(context->io_state, IO_INIT) /* Prepare the loading of the object. */ r = ifapi_keystore_load_async(&context->keystore, &context->io, path); return_if_error2(r, "Could not open: %s", path); fallthrough; statecase(context->io_state, IO_ACTIVE) /* Finalize or retry the reading and check the object type */ r = ifapi_keystore_load_finish(&context->keystore, &context->io, &object); return_try_again(r); return_if_error(r, "read_finish failed"); switch (object.objectType) { case IFAPI_KEY_OBJ: r = ifapi_get_name(&object.misc.key.public.publicArea, (TPM2B_NAME *)name); break; case IFAPI_EXT_PUB_KEY_OBJ: r = ifapi_get_name(&object.misc.ext_pub_key.public.publicArea, (TPM2B_NAME *)name); break; case IFAPI_NV_OBJ: r = ifapi_nv_get_name(&object.misc.nv.public, name); break; default: goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid object %s.", cleanup, path); } goto_if_error(r, "Get object name.", cleanup); break; statecasedefault(context->state); } cleanup: ifapi_cleanup_ifapi_object(&object); return r; } /** Get public data of a NV object from keystore. * * @param[in] path The relative path of the NV object. * @param[out] nv_public The caller allocated public structure. * @param[in,out] ctx The context to access io and keystore module and to store * the io state. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be loaded. * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for * internal operations or return parameters. * @retval TSS2_FAPI_RC_BAD_TEMPLATE If the loaded template is not * appropriate for this operation. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @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_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_GENERAL_FAILURE if an internal error occurred. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ TSS2_RC ifapi_get_nv_public( const char *path, TPM2B_NV_PUBLIC *nv_public, void *ctx) { TSS2_RC r = TSS2_RC_SUCCESS; IFAPI_OBJECT object; FAPI_CONTEXT *context = ctx; switch (context->io_state) { statecase(context->io_state, IO_INIT) /* Prepare the loading of the NV object. */ r = ifapi_keystore_load_async(&context->keystore, &context->io, path); return_if_error2(r, "Could not open: %s", path); fallthrough; statecase(context->io_state, IO_ACTIVE) /* Finalize or retry the reading and check the object type */ r = ifapi_keystore_load_finish(&context->keystore, &context->io, &object); return_try_again(r); return_if_error(r, "read_finish failed"); if (object.objectType != IFAPI_NV_OBJ) { goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Object %s is not a key.", cleanup, path); } *nv_public = object.misc.nv.public; context->io_state = IO_INIT; break; statecasedefault(context->state); } cleanup: ifapi_cleanup_ifapi_object(&object); return r; } /** Read values of PCR registers and clear selection. * * @param[in,out] pcr_select The registers to be read (bank selection from profile). * @param[in,out] pcr_selection The registers to be read (with bank selection). * @param[out] pcr_values The callee-allocated public structure. * @param[in,out] ctx The context to access io and keystore module and to store * the io state. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_BAD_VALUE if the input parameters had inappropriate values. * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet * complete. Call this function again later. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous * operation already pending. */ TSS2_RC ifapi_read_pcr( TPMS_PCR_SELECT *pcr_select, TPML_PCR_SELECTION *pcr_selection, TPML_PCRVALUES **pcr_values, void *ctx) { TSS2_RC r = TSS2_RC_SUCCESS; FAPI_CONTEXT *context = ctx; UINT32 update_counter; TPML_PCR_SELECTION *out_selection = NULL; TPML_PCR_SELECTION *profile_selection; TPML_DIGEST *pcr_digests = NULL; size_t i, pcr, n_pcrs = 0, i_pcr; switch (context->io_state) { statecase(context->io_state, IO_INIT) if (pcr_select->sizeofSelect) { if (pcr_selection->count) { /* If pcr_select is used pcr_selection can't be initialized */ return_error(TSS2_FAPI_RC_BAD_VALUE, "Policy PCR: pcr_selection can't be used if pcr_selection is used."); } /* Determine hash alg */ profile_selection = &context->profiles.default_profile.pcr_selection; for (i = 0; i < profile_selection->count; i++) { for (pcr = 0; pcr < TPM2_MAX_PCRS; pcr++) { uint8_t byte_idx = pcr / 8; uint8_t flag = 1 << (pcr % 8); /* Check whether PCR is used. */ if (flag & profile_selection->pcrSelections[i].pcrSelect[byte_idx] && flag & pcr_select->pcrSelect[byte_idx]) { pcr_selection->pcrSelections[0].hash = profile_selection->pcrSelections[i].hash; } } } if (!pcr_selection->pcrSelections[0].hash) { /* hash for current pcr_select can't be determined */ return_error(TSS2_FAPI_RC_BAD_VALUE, "Policy PCR: pcr_select does not match profile."); } /* Only one bank will be used. The hash alg from profile will be used */ pcr_selection->count = 1; pcr_selection->pcrSelections[0].sizeofSelect = pcr_select->sizeofSelect; for (i = 0; i < pcr_select->sizeofSelect; i++) pcr_selection->pcrSelections[0].pcrSelect[i] = pcr_select->pcrSelect[i]; } /* Prepare the PCR Reading. */ r = Esys_PCR_Read_Async(context->esys, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, pcr_selection); return_if_error(r, "PCR Read"); fallthrough; statecase(context->io_state, IO_ACTIVE) /* Finalize or retry the reading and check the object type */ r = Esys_PCR_Read_Finish(context->esys, &update_counter, &out_selection, &pcr_digests); if ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN) return TSS2_FAPI_RC_TRY_AGAIN; return_if_error(r, "PCR_Read_Finish"); /* Count pcrs */ for (i = 0; i < out_selection->count; i++) { for (pcr = 0; pcr < TPM2_MAX_PCRS; pcr++) { uint8_t byte_idx = pcr / 8; uint8_t flag = 1 << (pcr % 8); /* Check whether PCR is used. */ if (flag & out_selection->pcrSelections[i].pcrSelect[byte_idx]) n_pcrs += 1; } } *pcr_values = calloc(1, sizeof(TPML_PCRVALUES) + n_pcrs* sizeof(TPMS_PCRVALUE)); goto_if_null2(*pcr_values, "Out of memory.", r, TSS2_FAPI_RC_MEMORY, cleanup); /* Initialize digest list with pcr values from TPM */ i_pcr = 0; for (i = 0; i < out_selection->count; i++) { for (pcr = 0; pcr < TPM2_MAX_PCRS; pcr++) { uint8_t byte_idx = pcr / 8; uint8_t flag = 1 << (pcr % 8); /* Check whether PCR is used. */ if (flag & out_selection->pcrSelections[i].pcrSelect[byte_idx]) { (*pcr_values)->pcrs[i_pcr].pcr = pcr; (*pcr_values)->pcrs[i_pcr].hashAlg = out_selection->pcrSelections[i].hash; memcpy(&(*pcr_values)->pcrs[i_pcr].digest, &pcr_digests->digests[i_pcr].buffer[0], pcr_digests->digests[i_pcr].size); i_pcr += 1; } } } context->io_state = IO_INIT; break; statecasedefault(context->state); } cleanup: SAFE_FREE(out_selection); SAFE_FREE(pcr_digests); return r; } /** Callback for authorization of objects used by policy. * * @param[in] name The name of the object to be authorized. * @param[in] object_handle The ESYS handle of the used object. * @param[in] auth_handle will be used for object authorization. For keys it will we equal to the object handle. * @param[out] authSession The session used for object authorization. * @param[in,out] userdata The Fapi context which will be used for keystore * access, and storing the policy execution state. * the io state. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or policy is NULL. * @retval TSS2_FAPI_RC_MEMORY if memory allocation failed. * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet * complete. Call this function again later. * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous * operation already pending. * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy was not found. * @retval TSS2_FAPI_RC_KEY_NOT_FOUND If a key was not found. * @retval TSS2_FAPI_RC_IO_ERROR If an IO error occurred during reading * a policy or a key. * @retval TSS2_FAPI_RC_GENERAL_FAILURE If an error in an used library * occurred. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @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_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest * was not successful. * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. */ TSS2_RC ifapi_policyeval_cbauth( TPM2B_NAME *name, ESYS_TR *object_handle, ESYS_TR *auth_handle, ESYS_TR *authSession, void *userdata) { TSS2_RC r; FAPI_CONTEXT *fapi_ctx = userdata; IFAPI_POLICY_EXEC_CTX *current_policy; IFAPI_POLICY_EXEC_CB_CTX *cb_ctx; bool next_case; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); return_if_null(fapi_ctx->policy.policyutil_stack, "Policy not initialized.", TSS2_FAPI_RC_BAD_REFERENCE); if (fapi_ctx->policy.util_current_policy) { /* Use the current policy in the policy stack. */ current_policy = fapi_ctx->policy.util_current_policy->pol_exec_ctx; } else { /* Start with the bottom of the policy stack */ current_policy = fapi_ctx->policy.policyutil_stack->pol_exec_ctx; } cb_ctx = current_policy->app_data; do { next_case = false; switch (cb_ctx->cb_state) { statecase(cb_ctx->cb_state, POL_CB_EXECUTE_INIT); cb_ctx->auth_index = ESYS_TR_NONE; /* Search object with name in keystore. */ r = ifapi_keystore_search_obj(&fapi_ctx->keystore, &fapi_ctx->io, name, &cb_ctx->object_path); FAPI_SYNC(r, "Search Object", cleanup); r = ifapi_keystore_load_async(&fapi_ctx->keystore, &fapi_ctx->io, cb_ctx->object_path); return_if_error2(r, "Could not open: %s", cb_ctx->object_path); SAFE_FREE(cb_ctx->object_path); fallthrough; statecase(cb_ctx->cb_state, POL_CB_READ_OBJECT); /* Get object from file */ r = ifapi_keystore_load_finish(&fapi_ctx->keystore, &fapi_ctx->io, &cb_ctx->object); return_try_again(r); return_if_error(r, "read_finish failed"); r = ifapi_initialize_object(fapi_ctx->esys, &cb_ctx->object); goto_if_error(r, "Initialize NV object", cleanup); if (cb_ctx->object.objectType == IFAPI_NV_OBJ) { /* NV Authorization */ cb_ctx->nv_index = cb_ctx->object.handle; /* Determine the object used for authorization. */ get_nv_auth_object(&cb_ctx->object, cb_ctx->object.handle, &cb_ctx->auth_object, &cb_ctx->auth_index); goto_if_error(r, "PolicySecret set authorization", cleanup); cb_ctx->cb_state = POL_CB_AUTHORIZE_OBJECT; cb_ctx->auth_object_ptr = &cb_ctx->auth_object; next_case = true; break; } else if (cb_ctx->object.objectType == IFAPI_HIERARCHY_OBJ) { cb_ctx->cb_state = POL_CB_AUTHORIZE_OBJECT; next_case = true; break; } else { cb_ctx->key_handle = cb_ctx->object.handle; cb_ctx->cb_state = POL_CB_LOAD_KEY; } fallthrough; statecase(cb_ctx->cb_state, POL_CB_LOAD_KEY); /* Key loading and authorization */ r = ifapi_load_key(fapi_ctx, cb_ctx->object_path, &cb_ctx->auth_object_ptr); FAPI_SYNC(r, "Fapi load key.", cleanup); cb_ctx->object = *cb_ctx->key_object_ptr; SAFE_FREE(cb_ctx->key_object_ptr); cb_ctx->auth_object_ptr = &cb_ctx->object; fallthrough; statecase(cb_ctx->cb_state, POL_CB_AUTHORIZE_OBJECT); r = ifapi_authorize_object(fapi_ctx, cb_ctx->auth_object_ptr, authSession); return_try_again(r); goto_if_error(r, "Authorize object.", cleanup); cb_ctx->cb_state = POL_CB_EXECUTE_INIT; break; /* FALLTHRU */ statecasedefault(cb_ctx->cb_state); } } while (next_case); *object_handle = cb_ctx->object.handle; if (cb_ctx->object.objectType == IFAPI_NV_OBJ) *auth_handle = cb_ctx->auth_index; else *auth_handle = cb_ctx->object.handle; if (current_policy->policySessionSav != ESYS_TR_NONE) fapi_ctx->policy.session = current_policy->policySessionSav; cleanup: ifapi_cleanup_ifapi_object(&cb_ctx->object); if (current_policy->policySessionSav && current_policy->policySessionSav != ESYS_TR_NONE) fapi_ctx->policy.session = current_policy->policySessionSav; return r; } /** Callback for branch selection of policy or. * * @param[in] branches The list of policy branches. * @param[out] branch_idx The index of the selcted branch. * @param[in,out] userdata The Fapi context which will be used for keystore * access, and storing the policy execution state. * the io state. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context is NULL. * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if no branch selection callback is * defined. This callback will be needed of or policies which have to be * executed. * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the computed branch index * delivered by the callback does not identify a branch. */ TSS2_RC ifapi_branch_selection( TPML_POLICYBRANCHES *branches, size_t *branch_idx, void *userdata) { TSS2_RC r; FAPI_CONTEXT *fapi_ctx = userdata; size_t i; const char *names[8]; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); if (!fapi_ctx->callbacks.branch) { return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "No branch selection callback"); } for (i = 0; i < branches->count; i++) names[i] = branches->authorizations[i].name; r = fapi_ctx->callbacks.branch(fapi_ctx, "PolicyOR", &names[0], branches->count, branch_idx, fapi_ctx->callbacks.branchData); return_if_error(r, "policyBranchSelectionCallback"); if (*branch_idx >= branches->count) { return_error2(TSS2_FAPI_RC_AUTHORIZATION_FAILED, "Invalid branch number."); } return TSS2_RC_SUCCESS; } /** Callback for policy action. * * @param[in] action The name of the policy action. * @param[in,out] userdata The Fapi context which will be used for keystore * access, and storing the policy execution state. * the io state. * @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_REFERENCE If no user data is passed. */ TSS2_RC ifapi_policy_action( const char *action, void *userdata) { TSS2_RC r; FAPI_CONTEXT *fapi_ctx = userdata; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); if (!fapi_ctx->callbacks.action) { return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "No action callback"); } r = fapi_ctx->callbacks.action(fapi_ctx, action, fapi_ctx->callbacks.actionData); return_if_error(r, "ifapi_policy_action callback"); return TSS2_RC_SUCCESS; } /** Callback for signing a byte buffer. * * @param[in] key_pem The pem key used for signing operation. * @param[in] public_key_hint A human readable hint to denote which public * key to use. * @param[in] key_pem_hash_alg The hash alg used for digest computation. * @param[in] buffer the byte array to be signed. * @param[in] buffer_size The size of the buffer to be signed. * @param[out] signature The signature in DER format. * @param[out] signature_size The size of the signature. * @param[in] userdata The user context to retrieve the signing function. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN If the callback for signing is * not defined. * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data is passed. * @retval TSS2_FAPI_RC_TRY_AGAIN: if the callback operation is not yet * complete. Call this function again later. */ TSS2_RC ifapi_sign_buffer( char *key_pem, char *public_key_hint, TPMI_ALG_HASH key_pem_hash_alg, uint8_t *buffer, size_t buffer_size, uint8_t **signature, size_t *signature_size, void *userdata) { TSS2_RC r; FAPI_CONTEXT *fapi_ctx = userdata; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); if (!fapi_ctx->callbacks.sign) { return_error2(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "No signature callback."); } r = fapi_ctx->callbacks.sign(fapi_ctx, "PolicySigned", key_pem, public_key_hint ? public_key_hint : "", key_pem_hash_alg, buffer, buffer_size, signature, signature_size, fapi_ctx->callbacks.signData); try_again_or_error(r, "Execute policy signature callback."); return TSS2_RC_SUCCESS; } /** Check whether public data of key is assigned to policy. * * It will be checked whether policy was authorized by abort key with public * data of type TPMT_PUBLIC. * * @param[in] policy The policy to be checked. * @param[in] publicVoid The public information of the key. * @param[in] nameAlgVoid Not used for this compare function. * @param[out] equal Switch whether check was successful. */ static TSS2_RC equal_policy_authorization( TPMS_POLICY *policy, void *publicVoid, void *nameAlgVoid, bool *equal) { TPMT_PUBLIC *public = publicVoid; (void)nameAlgVoid; size_t i; TPML_POLICYAUTHORIZATIONS *authorizations = policy->policyAuthorizations; *equal = false; if (authorizations) { for (i = 0; i < authorizations->count; i++) { if (ifapi_TPMT_PUBLIC_cmp (public, &authorizations->authorizations[i].key)) { *equal = true; return TSS2_RC_SUCCESS; } } } return TSS2_RC_SUCCESS; } /** Check whether policy digest can be found in policy. * * It will be tested whether the policy has been instatiated with the * passed digest. * * @param[in] policy The policy to be checked. * @param[in] authPolicyVoid The digest to be searched. * @param[in] nameAlgVoid The hash algorithm used for the digest computation. * @param[out] equal Switch whether check was successful. */ static TSS2_RC compare_policy_digest( TPMS_POLICY *policy, void *authPolicyVoid, void *nameAlgVoid, bool *equal) { TPM2B_DIGEST *authPolicy = authPolicyVoid; TPMI_ALG_HASH *hash_alg_ptr = nameAlgVoid; TPMI_ALG_HASH hash_alg = *hash_alg_ptr; size_t i; TPML_DIGEST_VALUES *digest_values; *equal = false; digest_values = &policy->policyDigests; if (digest_values) { for (i = 0; i < digest_values->count; i++) { if (digest_values->digests[i].hashAlg == hash_alg) { if (memcmp(&digest_values->digests[i].digest, &authPolicy->buffer[0], authPolicy->size)) continue; *equal = true; return TSS2_RC_SUCCESS; } } } return TSS2_RC_SUCCESS; } /** Search a policy file which fulfills a certain predicate. * * @param[in] context The context for storing the state information of the search process and the keystore paths. * @param[in] compare The function which will be used for comparison. * @param[in] all_objects Switch which determines wheter all policies fulfilling the * the condition will be returned or only the first policy. * @param[in] object1 The first object used for comparison. * @param[in] object2 The second object used for comparison. * @param[out] policy_found The linked list with the policies fulfilling the condition. * * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest * was not successful. * @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_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_IO_ERROR if an error occurred while accessing the * object store. * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. * @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed. */ static TSS2_RC search_policy( FAPI_CONTEXT *context, Policy_Compare_Object compare, bool all_objects, void *object1, void *object2, struct POLICY_LIST **policy_found) { TSS2_RC r = TSS2_RC_SUCCESS; char *path; TPMS_POLICY policy = { 0 }; bool found; struct POLICY_LIST *policy_object = NULL; struct POLICY_LIST *second; switch (context->fsearch.state) { case FSEARCH_INIT: LOG_DEBUG("** STATE ** FSEARCH_INIT"); memset(&context->fsearch, 0, sizeof(IFAPI_FILE_SEARCH_CTX)); /* Get the list of all files. */ r = ifapi_keystore_list_all(&context->keystore, IFAPI_POLICY_DIR, &context->fsearch.pathlist, &context->fsearch.numPaths); return_if_error(r, "get entities."); context->fsearch.path_idx = context->fsearch.numPaths; context->fsearch.state = FSEARCH_OBJECT; /* FALLTHRU */ case FSEARCH_OBJECT: LOG_DEBUG("** STATE ** FSEARCH_OBJECT"); /* Test whether all files have been checked. */ if (context->fsearch.path_idx == 0) { if (*policy_found) { context->fsearch.state = FSEARCH_INIT; for (size_t i = 0; i < context->fsearch.numPaths; i++) { SAFE_FREE(context->fsearch.pathlist[i]); } SAFE_FREE(context->fsearch.pathlist); return TSS2_RC_SUCCESS; } goto_error(r, TSS2_FAPI_RC_POLICY_UNKNOWN, "Policy not found.", cleanup); } context->fsearch.path_idx -= 1; path = context->fsearch.pathlist[context->fsearch.path_idx]; context->fsearch.current_path = path; LOG_DEBUG("Check file: %s %zu", path, context->fsearch.path_idx); /* Prepare policy loading. */ r = ifapi_policy_store_load_async(&context->pstore, &context->io, path); goto_if_error2(r, "Can't open: %s", cleanup, path); context->fsearch.state = FSEARCH_READ; /* FALLTHRU */ case FSEARCH_READ: LOG_DEBUG("** STATE ** FSEARCH_READ"); /* Finalize policy loading if possible. */ r = ifapi_policy_store_load_finish(&context->pstore, &context->io, &policy); return_try_again(r); goto_if_error(r, "read_finish failed", cleanup); /* Call the passed compare function. */ r = compare(&policy, object1, object2, &found); if (found) { LOG_DEBUG("compare true %s", context->fsearch.pathlist[context->fsearch.path_idx]); } else { LOG_DEBUG("compare false %s", context->fsearch.pathlist[context->fsearch.path_idx]); } goto_if_error(r, "Invalid cipher object.", cleanup); if (!found) { if (!all_objects && context->fsearch.path_idx == 0) { /* All files checked, but no policy found. */ context->fsearch.state = FSEARCH_INIT; ifapi_cleanup_policy(&policy); return TSS2_BASE_RC_POLICY_UNKNOWN; } else { /* Continue search. */ context->fsearch.state = FSEARCH_OBJECT; ifapi_cleanup_policy(&policy); return TSS2_FAPI_RC_TRY_AGAIN; } } /* Extend linked list.*/ policy_object = calloc(sizeof(struct POLICY_LIST), 1); return_if_null(policy_object, "Out of memory.", TSS2_FAPI_RC_MEMORY); strdup_check(policy_object->path, context->fsearch.current_path, r, cleanup); policy_object->policy = policy; if (*policy_found != NULL) { second = *policy_found; policy_object->next = second; } *policy_found = policy_object; if (context->fsearch.path_idx == 0) { context->fsearch.state = FSEARCH_INIT; /* Cleanup list of all paths. */ for (size_t i = 0; i < context->fsearch.numPaths; i++) { SAFE_FREE(context->fsearch.pathlist[i]); } SAFE_FREE(context->fsearch.pathlist); return TSS2_RC_SUCCESS; } if (all_objects) { context->fsearch.state = FSEARCH_OBJECT; return TSS2_FAPI_RC_TRY_AGAIN; } break; default: context->state = _FAPI_STATE_INTERNALERROR; goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid state for load key.", cleanup); } context->fsearch.state = FSEARCH_INIT; for (size_t i = 0; i < context->fsearch.numPaths; i++) { SAFE_FREE(context->fsearch.pathlist[i]); } SAFE_FREE(context->fsearch.pathlist); return TSS2_RC_SUCCESS; cleanup: SAFE_FREE(policy_object); ifapi_cleanup_policy(&policy); for (size_t i = 0; i < context->fsearch.numPaths; i++) { SAFE_FREE(context->fsearch.pathlist[i]); } SAFE_FREE(context->fsearch.pathlist); context->fsearch.state = FSEARCH_INIT; return r; } /** Get policy digest for a certain hash alg. * * @param[in] policy The policy with the digest list. * @param[in] hashAlg The hash algorithm used for the digest computation. * @param[out] digest The digest matching hashAlg. * @retval TSS2_RC_SUCCESS on success. * @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. */ static TSS2_RC get_policy_digest(TPMS_POLICY *policy, TPMI_ALG_HASH hashAlg, TPM2B_DIGEST *digest) { size_t i; if (!(digest->size = ifapi_hash_get_digest_size(hashAlg))) { return_error2(TSS2_FAPI_RC_BAD_VALUE, "Unsupported hash algorithm (%" PRIu16 ")", hashAlg); } for (i = 0; i < policy->policyDigests.count; i++) { if (policy->policyDigests.digests[i].hashAlg == hashAlg) { memcpy(&digest->buffer[0], &policy->policyDigests.digests[i].digest, digest->size); return TSS2_RC_SUCCESS; } } return TSS2_FAPI_RC_GENERAL_FAILURE; } /** Get policy authorization for a certain public key * * @param[in] policy The policy with the authorization. * @param[in] public The public data of the key. * @param[out] signature The signature found in the authorization list. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. */ static TSS2_RC get_policy_signature( TPMS_POLICY *policy, TPMT_PUBLIC *public, TPMT_SIGNATURE *signature) { size_t i; for (i = 0; i < policy->policyAuthorizations->count; i++) { if (ifapi_TPMT_PUBLIC_cmp(public, &policy->policyAuthorizations->authorizations[i].key)) { *signature = policy->policyAuthorizations->authorizations[i].signature; return TSS2_RC_SUCCESS; } } /* Appropriate authorization should always exist */ return TSS2_FAPI_RC_GENERAL_FAILURE; } /** Cleanup a linked list of policies. * * @param[in] The linked list. */ static void cleanup_policy_list(struct POLICY_LIST * list) { if (list) { struct POLICY_LIST * branch = list; while (branch) { struct POLICY_LIST *next = branch->next; /* Cleanup the policy stored in the list. */ ifapi_cleanup_policy(&branch->policy); SAFE_FREE(branch->path); SAFE_FREE(branch); branch = next; } } } /** Callback for retrieving, selecting and execute a authorized policy. * * All policies authorized by a certain key will be retrieved and one policy * will be selected via a branch selection callback. * * @param[in] key_public the public data of the key which was used for policy * authorization. * @param[in] hash_alg The hash algorithm used for policy computation. * @param[out] digest The policy digest of the authorized policy. * @param[out] signature The signature produced during policy authorization. * @param[in] userdata The user context to retrieve the policy. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_MEMORY: if it's not possible to allocate enough memory. * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data id passed or context stack * is not initialized. * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy * store. * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found. * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest * was not successful. * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy * execution fails. * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback * is not set. * @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_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_AUTHORIZATION_FAILED if the authorization attempt fails. * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found. * @retval TSS2_ESYS_RC_* possible error codes of ESAPI. */ TSS2_RC ifapi_exec_auth_policy( TPMT_PUBLIC *key_public, TPMI_ALG_HASH hash_alg, TPM2B_DIGEST *digest, TPMT_SIGNATURE *signature, void *userdata) { TSS2_RC r; FAPI_CONTEXT *fapi_ctx = userdata; IFAPI_POLICY_EXEC_CTX *current_policy; IFAPI_POLICY_EXEC_CB_CTX *cb_ctx; size_t n, i; struct POLICY_LIST *branch; const char **names = NULL; size_t branch_idx; bool policy_set = false; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); return_if_null(fapi_ctx->policy.policyutil_stack, "Policy not initialized.", TSS2_FAPI_RC_BAD_REFERENCE); if (fapi_ctx->policy.util_current_policy) { /* Use the current policy in the policy stack. */ current_policy = fapi_ctx->policy.util_current_policy->pol_exec_ctx; } else { /* Start with the bottom of the policy stack */ current_policy = fapi_ctx->policy.policyutil_stack->pol_exec_ctx; } cb_ctx = current_policy->app_data; switch (cb_ctx->cb_state) { statecase(cb_ctx->cb_state, POL_CB_EXECUTE_INIT) current_policy->object_handle = ESYS_TR_NONE; current_policy->policy_list = NULL; fallthrough; statecase(cb_ctx->cb_state, POL_CB_SEARCH_POLICY) r = search_policy(fapi_ctx, equal_policy_authorization, true, key_public, NULL, ¤t_policy->policy_list); FAPI_SYNC(r, "Search policy", cleanup); if (current_policy->policy_list->next) { /* More than one policy has to be selected via callback */ if (!fapi_ctx->callbacks.branch) { return_error(TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN, "No branch selection callback"); } n = 1; /* Count policies */ for (branch = current_policy->policy_list; branch->next; branch = branch->next) n += 1; names = malloc(sizeof(char *) * n); return_if_null(names, "Out of memory.", TSS2_FAPI_RC_MEMORY); i = 0; branch = current_policy->policy_list; /* Compute name list for slectiion callback. */ do { names[i] = branch->path; i += 1; branch = branch->next; } while (branch); /* Policy selection */ r = fapi_ctx->callbacks.branch(fapi_ctx, "PolicyAuthorize", &names[0], n, &branch_idx, fapi_ctx->callbacks.branchData); goto_if_error(r, "policyBranchSelectionCallback", cleanup); if (branch_idx >= n) { goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid branch number.", cleanup); } /* Get policy from policy list */ n = 0; branch = current_policy->policy_list; do { if (n == branch_idx) { cb_ctx->policy = &branch->policy; policy_set = true; break; } n += 1; branch = branch->next; } while (branch); } else { /* Only one policy found. */ cb_ctx->policy = ¤t_policy->policy_list->policy; policy_set = true; } if (!policy_set) { goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Policy could not be set.", cleanup); } /* Prepare policy execution */ r = ifapi_policyutil_execute_prepare(fapi_ctx, current_policy->hash_alg, cb_ctx->policy); /* Next state will switch from prev context to next context. */ goto_if_error(r, "Prepare policy execution.", cleanup); fallthrough; statecase(cb_ctx->cb_state, POL_CB_EXECUTE_SUB_POLICY) ESYS_TR session = current_policy->session; r = ifapi_policyutil_execute(fapi_ctx, &session); if (r == TSS2_FAPI_RC_TRY_AGAIN){ SAFE_FREE(names); return r; } goto_if_error(r, "Execute policy.", cleanup); r = get_policy_signature(cb_ctx->policy, key_public, signature); goto_if_error(r, "Get authorization", cleanup); r = get_policy_digest(cb_ctx->policy, hash_alg, digest); goto_if_error(r, "Get authorization", cleanup); cb_ctx->cb_state = POL_CB_EXECUTE_INIT; break; statecasedefault_error(cb_ctx->state, r, cleanup); } cleanup: SAFE_FREE(names); cleanup_policy_list(current_policy->policy_list); return r; } /** Callback for executing a policy identified by a digest stored in a nv object. * * @param[in] nv_public the public data of the nv object which stores the digest * of the authorized policy. * @param[in] hash_alg The hash algorithm used for policy computation. * @param[in] userdata The user context to retrieve the policy. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_MEMORY: if it's not possible to allocate enough memory. * @retval TSS2_FAPI_RC_BAD_REFERENCE If no user data id passed or context stack * is not initialized. * @retval TSS2_FAPI_RC_IO_ERROR If an error occurs during access to the policy * store. * @retval TSS2_FAPI_RC_PATH_NOT_FOUND If a policy for a certain path was not found. * @retval TSS2_FAPI_RC_POLICY_UNKNOWN If policy search for a certain policy digest was * not successful. * @retval TPM2_RC_BAD_AUTH If the authentication for an object needed for policy * execution fails. * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. * @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_KEY_NOT_FOUND if a key was not found. * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred. * @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_exec_auth_nv_policy( TPM2B_NV_PUBLIC *nv_public, TPMI_ALG_HASH hash_alg, void *userdata) { TSS2_RC r; TPM2B_MAX_NV_BUFFER *aux_data; FAPI_CONTEXT *fapi_ctx = userdata; IFAPI_POLICY_EXEC_CTX *current_policy; IFAPI_POLICY_EXEC_CB_CTX *cb_ctx; char *nv_path = NULL; ESYS_CONTEXT *esys_ctx; size_t digest_size, offset; TPMT_HA nv_policy; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); return_if_null(fapi_ctx->policy.policyutil_stack, "Policy not initialized.", TSS2_FAPI_RC_BAD_REFERENCE); if (fapi_ctx->policy.util_current_policy) { /* Use the current policy in the policy stack. */ current_policy = fapi_ctx->policy.util_current_policy->pol_exec_ctx; } else { /* Start with the bottom of the policy stack */ current_policy = fapi_ctx->policy.policyutil_stack->pol_exec_ctx; } cb_ctx = current_policy->app_data; esys_ctx = fapi_ctx->esys; if (!(digest_size = ifapi_hash_get_digest_size(hash_alg))) { return_error2(TSS2_FAPI_RC_BAD_VALUE, "Unsupported hash algorithm (%" PRIu16 ")", hash_alg); } switch (cb_ctx->cb_state) { statecase(cb_ctx->cb_state, POL_CB_EXECUTE_INIT) /* Search a NV object with a certain NV indext stored in nv_public. */ r = ifapi_keystore_search_nv_obj(&fapi_ctx->keystore, &fapi_ctx->io, nv_public, &nv_path); FAPI_SYNC(r, "Search Object", cleanup); r = ifapi_keystore_load_async(&fapi_ctx->keystore, &fapi_ctx->io, nv_path); SAFE_FREE(nv_path); return_if_error2(r, "Could not open: %s", nv_path); fallthrough; statecase(cb_ctx->cb_state, POL_CB_NV_READ) /* Get object from file */ r = ifapi_keystore_load_finish(&fapi_ctx->keystore, &fapi_ctx->io, &cb_ctx->object); return_try_again(r); return_if_error(r, "read_finish failed"); r = ifapi_initialize_object(esys_ctx, &cb_ctx->object); goto_if_error(r, "Initialize NV object", cleanup); current_policy->nv_index = cb_ctx->object.handle; ifapi_cleanup_ifapi_object(&cb_ctx->object); get_nv_auth_object(&cb_ctx->object, current_policy->nv_index, ¤t_policy->auth_object, ¤t_policy->auth_handle); fallthrough; statecase(cb_ctx->cb_state, POL_CB_AUTHORIZE_OBJECT) /* Authorize the NV object with the corresponding auth object. */ r = ifapi_authorize_object(fapi_ctx, &cb_ctx->auth_object, &cb_ctx->session); return_try_again(r); goto_if_error(r, "Authorize object.", cleanup); /* Prepare the reading of the NV index from TPM. */ r = Esys_NV_Read_Async(esys_ctx, current_policy->auth_handle, current_policy->nv_index, cb_ctx->session, ESYS_TR_NONE, ESYS_TR_NONE, sizeof(TPMI_ALG_HASH) + digest_size, 0); goto_if_error(r, "Unmarshal policy", cleanup); fallthrough; statecase(cb_ctx->cb_state, POL_CB_READ_NV_POLICY) /* Finalize the reading. */ r = Esys_NV_Read_Finish(esys_ctx, &aux_data); try_again_or_error_goto(r, "NV read", cleanup); offset = 0; r = Tss2_MU_TPMT_HA_Unmarshal(&aux_data->buffer[0], aux_data->size, &offset, &nv_policy); Esys_Free(aux_data); goto_if_error(r, "Unmarshal policy", cleanup); cb_ctx->policy_digest.size = digest_size; memcpy(&cb_ctx->policy_digest.buffer[0], &nv_policy.digest, digest_size); fallthrough; statecase(cb_ctx->cb_state, POL_CB_SEARCH_POLICY) /* Search policy appropriate in object store */ r = search_policy(fapi_ctx, compare_policy_digest, false, &cb_ctx->policy_digest, &hash_alg, ¤t_policy->policy_list); FAPI_SYNC(r, "Search policy", cleanup); if (!current_policy->policy_list) { goto_error(r, TSS2_FAPI_RC_POLICY_UNKNOWN, "Policy not found", cleanup); } /* Prepare policy execution */ r = ifapi_policyutil_execute_prepare(fapi_ctx, current_policy->hash_alg, ¤t_policy->policy_list->policy); return_if_error(r, "Prepare policy execution."); fallthrough; statecase(cb_ctx->cb_state, POL_CB_EXECUTE_SUB_POLICY) ESYS_TR session = current_policy->session; r = ifapi_policyutil_execute(fapi_ctx, &session); if (r == TSS2_FAPI_RC_TRY_AGAIN) return r; goto_if_error(r, "Execute policy.", cleanup); cb_ctx->cb_state = POL_CB_EXECUTE_INIT; break; statecasedefault_error(cb_ctx->state, r, cleanup); } cleanup: cleanup_policy_list(current_policy->policy_list); SAFE_FREE(nv_path); return r; } /** Callback for getting the name of a key to be duplicated. * * @param[out] name the name of the object to be duplicated. * @param[in] userdata The user context to retrieve the key. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_FAPI_RC_BAD_REFERENCE: if the context is not passed or the * object to be duplicated is not set. */ TSS2_RC ifapi_get_duplicate_name( TPM2B_NAME *name, void *userdata) { FAPI_CONTEXT *fapi_ctx = userdata; return_if_null(fapi_ctx, "Bad user data.", TSS2_FAPI_RC_BAD_REFERENCE); return_if_null(fapi_ctx->duplicate_key, "Object for duplication no set.", TSS2_FAPI_RC_BAD_REFERENCE); *name = fapi_ctx->duplicate_key->misc.key.name; return TSS2_RC_SUCCESS; }