tpm2-util: add infra for allocating nvindex-based PCRs (aka "NvPCRs")

We'd like to measure various additional things into PCRs, but all
available ones to the OS are already used for various purposes. Hence,
let's introduce a new concept of "NV Index based PCRs", i.e. let's use
TPM2 nv indexes of type TPM2_NT_EXTEND that mostly behave like real
PCRs, but which we can allocate relatively freely from the nv index
space. Let's call these "fake" PCRs "NvPCRs".

My original intention was to get a fixed NV index range assigned from
the TCG, either for Linux or for systemd as a project, but this stalled
with no further updates from the TCG for more than a year and a half
now. I was told an NV index range to use though, even if it never was
officially assigned, hence this PR uses this by default. But the range
is configurable at build time, on purpose, so that downstreams have some
flexibility to change this if they want. To abstract the actual nvindex
number away we introduce a naming concept, so that nvindexes are
referenced by name string rather than number.

NvPCRs are defined in little JSON snippets in /usr/lib/nvpcr/*.nvpcr,
that match up index number and name, as well as pick a hash algorithm.

There's one complication: these nvindex (like any nvindex) can be
deleted by anyone with access to the TPM, and then be recreated. This
could be used to reset the NvPCRs to zero during runtime, which defeats
the whole point of them. Our way out: we measure a secret as first thing
after creation into the NvPCRs. (Or actually, we measure a per-NvPCR
secret we derive from a system secret via an HMAC of the NvPCR name) and
the nvindex handle). This "anchoring" secret is stored in /run/ +
/var/lib/ + ESP/XBOOTLDR (the latter encrypted as credential, locked to
the TPM), to make it available at the whole runtime of the OS.
This commit is contained in:
Lennart Poettering
2024-06-10 15:55:54 +02:00
parent b7e072f3fa
commit b0c5c6aad8
5 changed files with 1044 additions and 20 deletions

View File

@@ -1343,6 +1343,7 @@ tpm2 = dependency('tss2-esys tss2-rc tss2-mu tss2-tcti-device',
tpm2_cflags = tpm2.partial_dependency(includes: true, compile_args: true)
conf.set10('HAVE_TPM2', tpm2.found())
conf.set10('HAVE_TSS2_ESYS3', tpm2.found() and tpm2.version().version_compare('>= 3.0.0'))
conf.set('TPM2_NVPCR_BASE', get_option('tpm2-nvpcr-base'))
libdw = dependency('libdw',
required : get_option('elfutils'))
@@ -3028,6 +3029,7 @@ summary({
'default user $PATH' : default_user_path != '' ? default_user_path : '(same as system services)',
'systemd service watchdog' : service_watchdog == '' ? 'disabled' : service_watchdog,
'time epoch' : f'@time_epoch@ (@alt_time_epoch@)',
'TPM2 nvpcr base' : run_command(sh, '-c', 'printf 0x%x @0@'.format(get_option('tpm2-nvpcr-base')), check : true).stdout()
})
# TODO:

View File

@@ -449,6 +449,8 @@ option('libfido2', type : 'feature', deprecated : { 'true' : 'enabled', 'false'
description : 'FIDO2 support')
option('tpm2', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'TPM2 support')
option('tpm2-nvpcr-base', type : 'integer', value: 0x01d10200,
description : 'Base for TPM2 nvindex based PCRs')
option('elfutils', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },
description : 'elfutils support')
option('zlib', type : 'feature', deprecated : { 'true' : 'enabled', 'false' : 'disabled' },

File diff suppressed because it is too large Load Diff

View File

@@ -150,6 +150,11 @@ const char* tpm2_userspace_event_type_to_string(Tpm2UserspaceEventType type) _co
Tpm2UserspaceEventType tpm2_userspace_event_type_from_string(const char *s) _pure_;
int tpm2_pcr_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event, const char *description);
int tpm2_nvpcr_get_index(const char *name, uint32_t *ret);
int tpm2_nvpcr_extend_bytes(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
int tpm2_nvpcr_acquire_anchor_secret(struct iovec *ret, bool sync_secondary);
int tpm2_nvpcr_initialize(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *anchor_secret);
int tpm2_nvpcr_read(Tpm2Context *c, const Tpm2Handle *session, const char *name, struct iovec *ret, uint32_t *ret_nv_index);
uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s);
void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
@@ -296,7 +301,10 @@ int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fing
int tpm2_define_policy_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE requested_nv_index, const TPM2B_DIGEST *write_policy, 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_define_nvpcr_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, TPMI_ALG_HASH algorithm, Tpm2Handle **ret_nv_handle);
int tpm2_extend_nvpcr_nv_index(Tpm2Context *c, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, const struct iovec *digest);
int tpm2_undefine_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle);
int tpm2_read_nv_index(Tpm2Context *c, const Tpm2Handle *session, TPM2_HANDLE nv_index, const Tpm2Handle *nv_handle, struct iovec *ret_value);
int tpm2_seal_data(Tpm2Context *c, const struct iovec *data, const Tpm2Handle *primary_handle, const Tpm2Handle *encryption_session, const TPM2B_DIGEST *policy, struct iovec *ret_public, struct iovec *ret_private);
int tpm2_unseal_data(Tpm2Context *c, const struct iovec *public, const struct iovec *private, const Tpm2Handle *primary_handle, const Tpm2Handle *policy_session, const Tpm2Handle *encryption_session, struct iovec *ret_data);
@@ -510,3 +518,5 @@ const char* tpm2_pcr_index_to_string(int pcr) _const_;
assert_cc(TPM2_NV_INDEX_UNASSIGNED_FIRST >= TPM2_NV_INDEX_FIRST);
assert_cc(TPM2_NV_INDEX_UNASSIGNED_LAST <= TPM2_NV_INDEX_LAST);
#endif
bool tpm2_nvpcr_name_is_valid(const char *name);

View File

@@ -25,6 +25,23 @@ executables += [
generator_template + {
'name' : 'systemd-tpm2-generator',
'sources' : files('tpm2-generator.c'),
'conditions' : [
'ENABLE_BOOTLOADER',
'HAVE_OPENSSL',
'HAVE_TPM2',
],
},
]
if conf.get('ENABLE_BOOTLOADER') == 1 and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_TPM2') == 1
nvpcrs = []
foreach n : nvpcrs
custom_target(
input : 'nvpcr/' + n + '.nvpcr.in',
output : n + '.nvpcr',
command : [jinja2_cmdline, '@INPUT@', '@OUTPUT@'],
install : true,
install_dir : prefixdir / 'lib/nvpcr')
endforeach
endif