From: Lennart Poettering Date: Mon, 23 Oct 2023 08:28:59 +0000 (+0200) Subject: tpm2-util: add helper for creating/removing/updating NV index with stored policy X-Git-Tag: v255-rc1~27^2~10 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=48d060564a9f796a311cad366b1edc9f5ee91ec5;p=thirdparty%2Fsystemd.git tpm2-util: add helper for creating/removing/updating NV index with stored policy This is the primary core of what pcrlock is supposed to do eventually: maintain a TPM2 policy hash inside an NV index which we then can reference via a PolicyAuthorizeNV expression to lock other objects against it. --- diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 60254d1cc22..1e776938a43 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4739,7 +4739,187 @@ int tpm2_unseal(Tpm2Context *c, return 0; } -#endif +static TPM2_HANDLE generate_random_nv_index(void) { + return TPM2_NV_INDEX_FIRST + (TPM2_HANDLE) random_u64_range(TPM2_NV_INDEX_LAST - TPM2_NV_INDEX_FIRST + 1); +} + +int tpm2_define_policy_nv_index( + Tpm2Context *c, + const Tpm2Handle *session, + TPM2_HANDLE requested_nv_index, + const TPM2B_DIGEST *write_policy, + const char *pin, + const TPM2B_AUTH *auth, + TPM2_HANDLE *ret_nv_index, + Tpm2Handle **ret_nv_handle, + TPM2B_NV_PUBLIC *ret_nv_public) { + + _cleanup_(tpm2_handle_freep) Tpm2Handle *new_handle = NULL; + TSS2_RC rc; + int r; + + assert(c); + assert(pin || auth); + + r = tpm2_handle_new(c, &new_handle); + if (r < 0) + return r; + + new_handle->flush = false; /* This is a persistent NV index, don't flush hence */ + + TPM2B_AUTH _auth = {}; + CLEANUP_ERASE(_auth); + + if (!auth) { + r = tpm2_get_pin_auth(TPM2_ALG_SHA256, pin, &_auth); + if (r < 0) + return r; + + auth = &_auth; + } + + for (unsigned try = 0; try < 25U; try++) { + TPM2_HANDLE nv_index; + + if (requested_nv_index != 0) + nv_index = requested_nv_index; + else + nv_index = generate_random_nv_index(); + + TPM2B_NV_PUBLIC public_info = { + .size = sizeof_field(TPM2B_NV_PUBLIC, nvPublic), + .nvPublic = { + .nvIndex = nv_index, + .nameAlg = TPM2_ALG_SHA256, + .attributes = TPM2_NT_ORDINARY | TPMA_NV_WRITEALL | TPMA_NV_POLICYWRITE | TPMA_NV_OWNERREAD, + .dataSize = offsetof(TPMT_HA, digest) + tpm2_hash_alg_to_size(TPM2_ALG_SHA256), + }, + }; + + if (write_policy) + public_info.nvPublic.authPolicy = *write_policy; + + rc = sym_Esys_NV_DefineSpace( + c->esys_context, + /* authHandle= */ ESYS_TR_RH_OWNER, + /* shandle1= */ session ? session->esys_handle : ESYS_TR_PASSWORD, + /* shandle2= */ ESYS_TR_NONE, + /* shandle3= */ ESYS_TR_NONE, + auth, + &public_info, + &new_handle->esys_handle); + + if (rc == TSS2_RC_SUCCESS) { + log_debug("NV Index 0x%" PRIx32 " successfully allocated.", nv_index); + + if (ret_nv_index) + *ret_nv_index = nv_index; + + if (ret_nv_handle) + *ret_nv_handle = TAKE_PTR(new_handle); + + if (ret_nv_public) + *ret_nv_public = public_info; + + return 0; + } + if (rc != TPM2_RC_NV_DEFINED) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to allocate NV index: %s", sym_Tss2_RC_Decode(rc)); + + if (requested_nv_index != 0) { + assert(nv_index == requested_nv_index); + return log_debug_errno(SYNTHETIC_ERRNO(EEXIST), + "Requested NV index 0x%" PRIx32 " already taken.", requested_nv_index); + } + + log_debug("NV index 0x%" PRIu32 " already taken, trying another one (%u tries left)", nv_index, try); + } + + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Too many attempts trying to allocate NV index: %s", sym_Tss2_RC_Decode(rc)); +} + +int tpm2_write_policy_nv_index( + Tpm2Context *c, + const Tpm2Handle *policy_session, + TPM2_HANDLE nv_index, + const Tpm2Handle *nv_handle, + const TPM2B_DIGEST *policy_digest) { + + TSS2_RC rc; + + assert(c); + assert(policy_session); + assert(nv_handle); + assert(policy_digest); + + if (policy_digest->size != tpm2_hash_alg_to_size(TPM2_ALG_SHA256)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Policy to store in NV index has wrong size."); + + TPMT_HA ha = { + .hashAlg = TPM2_ALG_SHA256, + }; + assert(policy_digest->size <= sizeof_field(TPMT_HA, digest)); + memcpy_safe(&ha.digest, policy_digest->buffer, policy_digest->size); + + TPM2B_MAX_NV_BUFFER buffer = {}; + size_t written = 0; + rc = sym_Tss2_MU_TPMT_HA_Marshal(&ha, buffer.buffer, sizeof(buffer.buffer), &written); + if (rc != TSS2_RC_SUCCESS) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to marshal policy digest."); + + buffer.size = written; + + rc = sym_Esys_NV_Write( + c->esys_context, + /* authHandle= */ nv_handle->esys_handle, + /* nvIndex= */ nv_handle->esys_handle, + /* shandle1= */ policy_session->esys_handle, + /* shandle2= */ ESYS_TR_NONE, + /* shandle3= */ ESYS_TR_NONE, + &buffer, + /* offset= */ 0); + if (rc != TSS2_RC_SUCCESS) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to write NV index: %s", sym_Tss2_RC_Decode(rc)); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *h = NULL; + h = hexmem(policy_digest->buffer, policy_digest->size); + log_debug("Written policy digest %s to NV index 0x%x", strnull(h), nv_index); + } + + return 0; +} + +int tpm2_undefine_policy_nv_index( + Tpm2Context *c, + const Tpm2Handle *session, + TPM2_HANDLE nv_index, + const Tpm2Handle *nv_handle) { + + TSS2_RC rc; + + assert(c); + assert(nv_handle); + + rc = sym_Esys_NV_UndefineSpace( + c->esys_context, + /* authHandle= */ ESYS_TR_RH_OWNER, + /* nvIndex= */ nv_handle->esys_handle, + /* shandle1= */ session ? session->esys_handle : ESYS_TR_NONE, + /* shandle2= */ ESYS_TR_NONE, + /* shandle3= */ ESYS_TR_NONE); + if (rc != TSS2_RC_SUCCESS) + return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to undefine NV index: %s", sym_Tss2_RC_Decode(rc)); + + log_debug("Undefined NV index 0x%x", nv_index); + return 0; +} +#endif /* HAVE_TPM2 */ int tpm2_list_devices(void) { #if HAVE_TPM2 diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 7e13fad567b..71ddfc514a8 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -234,6 +234,10 @@ int tpm2_tpm2b_public_from_openssl_pkey(const EVP_PKEY *pkey, TPM2B_PUBLIC *ret) int tpm2_tpm2b_public_from_pem(const void *pem, size_t pem_size, TPM2B_PUBLIC *ret); int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fingerprint, size_t *ret_fingerprint_size); +int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, const char *pin, const TPM2B_AUTH *auth, TPM2_HANDLE *ret_nv_index, Tpm2Handle **ret_nv_handle, TPM2B_NV_PUBLIC *ret_nv_public); +int tpm2_write_policy_nv_index(Tpm2Context *c, const Tpm2Handle *policy_session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const TPM2B_DIGEST *policy_digest); +int tpm2_undefine_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle); + int tpm2_serialize(Tpm2Context *c, const Tpm2Handle *handle, void **ret_serialized, size_t *ret_serialized_size); int tpm2_deserialize(Tpm2Context *c, const void *serialized, size_t serialized_size, Tpm2Handle **ret_handle);