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.
This commit is contained in:
+181
-1
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user