diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index f4bf3f2d7a..1effdcbf4a 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -1040,6 +1040,32 @@ The Trusted Platform Module's (TPM) NV index support is too limited to properly implement NV index backed additional PCRs. NvPCRs will not be allocated or initialized, and will not be available on the system. +-- 7a427613a56946b6bfa9aee1dfc553cd +Subject: Initialization of NvPCR not authorized in this boot +Defined-By: systemd +Support: %SUPPORT_URL% +Documentation: man:systemd-measure(1) man:ukify(1) man:systemd-tpm2-setup.service(8) + +An additional NV index backed Platform Configuration Register (PCR) could not be +initialized, because the authorization required for its initializing write is +not available. The initializing write to an NvPCR must be authorized by a signed +PCR policy with the "initrd" policy reference, which is only possible from the +initrd. This happens if the booted unified kernel image (UKI) does not carry a +PCR public key (in its .pcrpkey section), if it does not carry a signed PCR +policy (in its .pcrsig section) for that key with the "initrd" policy +reference, or if initialization was attempted after the system transitioned +from the initrd to the host, for example because +systemd-tpm2-setup-early.service is not included in the initrd. + +This is not an error: the affected NvPCRs will not be available during this +boot, and measurements to them will be skipped. + +To make NvPCRs available, sign the UKI's PCR policy for the initrd boot phase +with the "initrd" policy reference, for example via the --policyref=initrd and +--phase=enter-initrd options of systemd-measure, or the corresponding options +of ukify, and make sure systemd-tpm2-setup-early.service is included in the +initrd. + -- eac6d394c925493deac2ba99f3c2bb11 Subject: An optional dependency for @FEATURE@ is not installed Defined-By: systemd diff --git a/src/pcrextend/pcrextend.c b/src/pcrextend/pcrextend.c index f0cc7500f9..9d772abe69 100644 --- a/src/pcrextend/pcrextend.c +++ b/src/pcrextend/pcrextend.c @@ -365,8 +365,8 @@ static int extend_nvpcr_now( secret, event, safe); - if (r == -ENOBUFS) - return r; /* NV space exhausted; let caller handle gracefully */ + if (IN_SET(r, -ENOBUFS, -ENETDOWN)) + return r; /* NV space exhausted or NvPCR not initialized; let caller handle gracefully */ if (r < 0) return log_error_errno(r, "Could not extend NvPCR: %m"); @@ -451,7 +451,7 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va if (p.nvpcr) { r = extend_nvpcr_now(p.nvpcr, extend_iovec, &p.secret, p.event_type); - if (IN_SET(r, -ENOENT, -ENODEV)) + if (IN_SET(r, -ENOENT, -ENODEV, -ENETDOWN)) return sd_varlink_error(link, "io.systemd.PCRExtend.NoSuchNvPCR", NULL); if (r == -ENOBUFS) return sd_varlink_error(link, "io.systemd.PCRExtend.NvPCRSpaceExhausted", NULL); @@ -590,17 +590,25 @@ static int run(int argc, char *argv[]) { r = extend_nvpcr_now(arg_nvpcr_name, &IOVEC_MAKE(word, strlen(word)), NULL, event); else r = extend_pcr_now(arg_pcr_mask, &IOVEC_MAKE(word, strlen(word)), NULL, event); - /* Both extend paths report "TPM cannot be used for this measurement" (no PCR bank, missing crypto, - * no TPM device — see tpm2_context_new_for_measurement()) as -EOPNOTSUPP. Under --graceful we skip - * those rather than fail and block boot. Genuine faults keep their own errno and are never - * suppressed. */ - if (arg_graceful && r == -EOPNOTSUPP) { - log_notice_errno(r, "TPM2 cannot be used for measurement (no usable PCR bank, missing device, or missing crypto support), skipping gracefully."); - return EXIT_SUCCESS; - } - if (arg_graceful && r == -ENOBUFS) { - log_notice_errno(r, "TPM NV index space is exhausted, NvPCR '%s' could not be initialized, skipping gracefully.", arg_nvpcr_name); - return EXIT_SUCCESS; + if (arg_graceful) { + /* Both extend paths report "TPM cannot be used for this measurement" (no PCR bank, missing crypto, + * no TPM device — see tpm2_context_new_for_measurement()) as -EOPNOTSUPP. Under --graceful we skip + * those rather than fail and block boot. Genuine faults keep their own errno and are never + * suppressed. */ + if (r == -EOPNOTSUPP) { + log_notice_errno(r, "TPM2 cannot be used for measurement (no usable PCR bank, missing device, or missing crypto support), skipping gracefully."); + return EXIT_SUCCESS; + } + if (r == -ENETDOWN) { + /* The NvPCR couldn't be initialized in this boot (e.g. because the booted kernel image + * carries no suitable PCR signature), hence there's nothing to extend. */ + log_notice_errno(r, "NvPCR '%s' is not initialized, skipping gracefully.", arg_nvpcr_name); + return EXIT_SUCCESS; + } + if (r == -ENOBUFS) { + log_notice_errno(r, "TPM NV index space is exhausted, NvPCR '%s' could not be initialized, skipping gracefully.", arg_nvpcr_name); + return EXIT_SUCCESS; + } } if (r < 0) return r; diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index e76d0c6977..14bd26de27 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -4645,8 +4645,13 @@ static int find_signature( const char *k; int r; + assert(pcr_selection); + assert(policy || !ret_signature); + assert(!ret_signature || ret_signature_size); + /* Searches for a signature blob in the specified JSON object. Search keys are PCR bank, PCR mask, - * public key, and policy digest. */ + * public key, policy reference and policy digest. If 'policy' is NULL, matches any policy digest. + * If 'ret_signature' is NULL, only checks whether a matching entry exists. */ if (!sd_json_variant_is_object(v)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Signature is not a JSON object."); @@ -4716,18 +4721,23 @@ static int find_signature( if (!polj) continue; - r = sd_json_variant_unhex(polj, &polj_data, &polj_size); - if (r < 0) - return log_debug_errno(r, "Failed to decode policy hash JSON data: %m"); + if (policy) { + r = sd_json_variant_unhex(polj, &polj_data, &polj_size); + if (r < 0) + return log_debug_errno(r, "Failed to decode policy hash JSON data: %m"); - if (memcmp_nn(policy, policy_size, polj_data, polj_size) != 0) - continue; + if (memcmp_nn(policy, policy_size, polj_data, polj_size) != 0) + continue; + } /* This entry matches all our expectations, now return the signature included in it */ sigj = sd_json_variant_by_key(i, "sig"); if (!sigj) continue; + if (!ret_signature) + return 0; + return sd_json_variant_unbase64(sigj, ret_signature, ret_signature_size); } @@ -9130,8 +9140,13 @@ int tpm2_nvpcr_extend_bytes( return r; /* The NvPCR isn't initialized yet, i.e. systemd-tpm2-setup hasn't run. - * Initialize it now and extend again. */ + * Initialize it now and extend again. This can only run in the initrd. */ + if (!in_initrd()) + return log_debug_errno(r, "NvPCR is not initialized and lazy initialization is only available in the initrd, refusing."); + r = tpm2_nvpcr_initialize(c, session, name); + if (r == -ENOKEY) /* Can't be initialized in this boot, report it as not initialized. */ + return -ENETDOWN; if (r < 0) return log_debug_errno(r, "Failed to initialize NvPCR '%s': %m", name); @@ -9223,6 +9238,9 @@ int tpm2_nvpcr_initialize( return 0; } + if (!in_initrd()) + return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), "NvPCR '%s' is not initialized yet and initialization is only available in the initrd, refusing.", name); + r = dlopen_libcrypto(LOG_DEBUG); if (r < 0) return r; @@ -9256,6 +9274,41 @@ int tpm2_nvpcr_initialize( TPM2B_PUBLIC public; _cleanup_(iovec_done) struct iovec fingerprint = {}; r = tpm2_nvpcr_load_pcr_public_key(/* path= */ NULL, &public, &fingerprint); + if (r == -ENOENT) + /* Avoid failing systemd-tpm2-setup if there is no PCR public key attached to the UKI. */ + return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), + "No PCR public key available, cannot initialize NvPCR '%s'.", name); + if (r < 0) + return r; + + /* Load the signed PCR policy, which authorizes the initializing write. */ + _cleanup_(sd_json_variant_unrefp) sd_json_variant *signature_json = NULL; + r = tpm2_load_pcr_signature(/* path= */ NULL, &signature_json); + if (r == -ENOENT) + /* Avoid failing systemd-tpm2-setup if there are no PCR signatures attached to the UKI. */ + return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), + "No signed PCR policy available, cannot initialize NvPCR '%s'.", name); + if (r < 0) + return log_debug_errno(r, "Failed to load PCR signature for NvPCR initialization: %m"); + + /* Check that there's a signed policy for the initializing write at all before we allocate anything + * on the TPM. This avoids failing systemd-tpm2-setup if there are no policies signed for the "initrd" + * policy reference. We don't check the policy digest here: if there is a matching policy but it doesn't + * match the current PCR state, or its signature is invalid, then that's a real error which we + * want to report below. */ + TPML_PCR_SELECTION pcr_selection; + tpm2_tpml_pcr_selection_from_mask(NVPCR_PUBKEY_PCRMASK, TPM2_ALG_SHA256, &pcr_selection); + r = find_signature( + signature_json, + &pcr_selection, + fingerprint.iov_base, fingerprint.iov_len, + NVPCR_INIT_POLICY_REF, + /* policy= */ NULL, /* policy_size= */ 0, + /* ret_signature= */ NULL, /* ret_signature_size= */ NULL); + if (r == -ENOSTR) + return log_debug_errno(SYNTHETIC_ERRNO(ENOKEY), + "No signed PCR policy with reference '%s' available, cannot initialize NvPCR '%s'.", + NVPCR_INIT_POLICY_REF, name); if (r < 0) return r; @@ -9276,12 +9329,6 @@ int tpm2_nvpcr_initialize( log_debug("Successfully acquired handle to NV index 0x%" PRIx32 ".", p.nv_index); - /* Load the signed PCR policy, which authorizes the initializing write. */ - _cleanup_(sd_json_variant_unrefp) sd_json_variant *signature_json = NULL; - r = tpm2_load_pcr_signature(/* path= */ NULL, &signature_json); - if (r < 0) - return log_debug_errno(r, "Failed to load PCR signature for NvPCR initialization: %m"); - bool reset_marker; for (unsigned i = RETRY_NVPCR_INIT_MAX;; i--) { /* Open a policy session to perform the initializing write. */ diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index 25cf03e18e..c59291abfa 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -314,6 +314,9 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_TPM_NVPCR_UNSUPPORTED SD_ID128_MAKE(8f,07,a5,b8,14,ca,47,62,b8,9f,cc,30,82,e4,8a,ed) #define SD_MESSAGE_TPM_NVPCR_UNSUPPORTED_STR SD_ID128_MAKE_STR(8f,07,a5,b8,14,ca,47,62,b8,9f,cc,30,82,e4,8a,ed) +#define SD_MESSAGE_TPM_NVPCR_UNAUTHORIZED SD_ID128_MAKE(7a,42,76,13,a5,69,46,b6,bf,a9,ae,e1,df,c5,53,cd) +#define SD_MESSAGE_TPM_NVPCR_UNAUTHORIZED_STR SD_ID128_MAKE_STR(7a,42,76,13,a5,69,46,b6,bf,a9,ae,e1,df,c5,53,cd) + #define SD_MESSAGE_MISSING_DEPENDENCY SD_ID128_MAKE(ea,c6,d3,94,c9,25,49,3d,ea,c2,ba,99,f3,c2,bb,11) #define SD_MESSAGE_MISSING_DEPENDENCY_STR SD_ID128_MAKE_STR(ea,c6,d3,94,c9,25,49,3d,ea,c2,ba,99,f3,c2,bb,11) diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c index d9f6ebe46f..2cba21ff1a 100644 --- a/src/tpm2-setup/tpm2-setup.c +++ b/src/tpm2-setup/tpm2-setup.c @@ -417,6 +417,7 @@ typedef struct SetupNvPCRContext { Tpm2Context *tpm2_context; size_t n_already, n_initialized, n_failed, n_skipped; bool nv_space_exhausted; /* Set once the TPM ran out of NV index space, so we skip the rest. */ + bool nvpcr_unauthorized; /* Set if NvPCR initialization is not authorized in this boot, so we skip the rest. */ Set *done; } SetupNvPCRContext; @@ -448,6 +449,12 @@ static int setup_nvpcr_one( return 0; } + if (c->nvpcr_unauthorized) { + log_debug("NvPCR initialization is not authorized in this boot, skipping allocation of NvPCR '%s'.", name); + c->n_skipped++; + return 0; + } + r = tpm2_nvpcr_initialize(c->tpm2_context, /* session= */ NULL, name); if (r == -EOPNOTSUPP) { c->n_failed++; @@ -465,6 +472,17 @@ static int setup_nvpcr_one( LOG_MESSAGE("The TPM's NV index space is exhausted, skipping allocation of NvPCR '%s' and any less important ones: %m", name), LOG_MESSAGE_ID(SD_MESSAGE_TPM_NVINDEX_EXHAUSTED_STR)); } + if (r == -ENOKEY) { + /* NvPCR initialization is not authorized in this boot, because the booted UKI carries no + * PCR public key or no signed PCR policy for NvPCR initialization, or because we're not + * running in the initrd. This is not an error, the NvPCR simply won't be available in + * this boot. */ + c->nvpcr_unauthorized = true; + c->n_skipped++; + return log_struct_errno(LOG_NOTICE, r, + LOG_MESSAGE("NvPCR initialization is not authorized in this boot (no PCR public key, no signed PCR policy for NvPCR initialization, or not running in the initrd), unable to allocate NvPCR '%s' and any others: %m", name), + LOG_MESSAGE_ID(SD_MESSAGE_TPM_NVPCR_UNAUTHORIZED_STR)); + } if (r < 0) { c->n_failed++; return log_error_errno(r, "Failed to initialize NvPCR index: %m"); @@ -564,6 +582,9 @@ static int setup_nvpcr(void) { if (c.nv_space_exhausted) log_notice("Skipped %zu lowest-priority NvPCR(s) because the TPM's NV index space is exhausted, proceeding anyway.", c.n_skipped); + if (c.nvpcr_unauthorized) + log_notice("Skipped %zu NvPCR(s) because NvPCR initialization is not authorized in this boot, proceeding anyway.", c.n_skipped); + if (c.n_failed > 0) log_warning("%zu NvPCRs failed to initialize, proceeding anyway.", c.n_failed); @@ -583,6 +604,8 @@ static int setup_nvpcr(void) { return EX_UNAVAILABLE; /* e.g. no NvPCR support in TPM */ if (ret == -ENOBUFS) return EX_CANTCREAT; /* NV index space on TPM exhausted */ + if (ret == -ENOKEY) + return EX_CONFIG; /* NvPCR initialization not authorized in this boot */ return ret; } diff --git a/test/units/TEST-70-TPM2.nvpcr.sh b/test/units/TEST-70-TPM2.nvpcr.sh index 60dac06c48..4fdc1848de 100755 --- a/test/units/TEST-70-TPM2.nvpcr.sh +++ b/test/units/TEST-70-TPM2.nvpcr.sh @@ -24,10 +24,10 @@ at_exit() { mv -f /run/systemd/tpm2-pcr-public-key.pem.bak /run/systemd/tpm2-pcr-public-key.pem mv -f /run/systemd/tpm2-pcr-signature.json.bak /run/systemd/tpm2-pcr-signature.json - rm -f /tmp/tpm2-pcr-private-key.pem + rm -f /tmp/tpm2-pcr-private-key.pem /tmp/tpm2-pcr-public-key.pem /tmp/tpm2-pcr-signature.json.valid rm -rf /run/nvpcr /tmp/nvpcr rm -f /var/tmp/nvpcr.raw /run/verity.d/test-70-nvpcr.crt - rm -f /run/systemd/nvpcr/test.auth /run/systemd/nvpcr/test2.auth /run/systemd/nvpcr/aaa.auth /run/systemd/nvpcr/zzz.auth + rm -f /run/systemd/nvpcr/test.auth /run/systemd/nvpcr/test2.auth /run/systemd/nvpcr/aaa.auth /run/systemd/nvpcr/zzz.auth /run/systemd/nvpcr/unauth.auth /run/systemd/nvpcr/unauth2.auth rm -f /tmp/test.policy } @@ -35,10 +35,11 @@ trap at_exit EXIT # systemd-tpm2-setup returns EX_UNAVAILABLE rather than 0 when it cannot set something up but this # is still considered success. This happens at the moment because there is no EK certificate in -# QEMU guests. +# QEMU guests. NvPCRs can only be initialized from the initrd, so pretend we're running there, as +# systemd-tpm2-setup-early.service would be. run_tpm2_setup() { local rc=0 - "$SD_TPM2SETUP" --early=yes || rc=$? + SYSTEMD_IN_INITRD=1 "$SD_TPM2SETUP" --early=yes || rc=$? [[ "$rc" -eq 0 || "$rc" -eq 69 ]] } @@ -156,11 +157,121 @@ AAA_LINE="$(echo "$SETUP_LOG" | grep -n "Setting up NvPCR 'aaa'" | cut -d: -f1)" ZZZ_LINE="$(echo "$SETUP_LOG" | grep -n "Setting up NvPCR 'zzz'" | cut -d: -f1)" test "$ZZZ_LINE" -lt "$AAA_LINE" +# Verify that systemd-tpm2-setup skips (rather than fails) NvPCR initialization if the booted kernel +# image carries no PCR public key or no signed PCR policy with the "initrd" reference, and that it +# doesn't allocate the NV index in that case. systemd-pcrextend must then treat the NvPCR as not +# initialized. 'unauth2' sorts after 'unauth', so once 'unauth' couldn't be initialized it must be +# skipped without another initialization attempt. +cat >/run/nvpcr/unauth.nvpcr </run/nvpcr/unauth2.nvpcr <&1)" || rc=$? + [[ "$rc" -eq 78 ]] + grep -F "$expected" <<<"$log" >/dev/null + grep -F "Skipped 2 NvPCR(s) because NvPCR initialization is not authorized in this boot" <<<"$log" >/dev/null + grep -F "skipping allocation of NvPCR 'unauth2'." <<<"$log" >/dev/null + (! grep -F "cannot initialize NvPCR 'unauth2'" <<<"$log" >/dev/null) + test ! -f /run/systemd/nvpcr/unauth.auth + test ! -f /run/systemd/nvpcr/unauth2.auth + (! tpm2_nvreadpublic 0x01d10216) + (! tpm2_nvreadpublic 0x01d10217) + + (! "$SD_PCREXTEND" --nvpcr=unauth foo) + "$SD_PCREXTEND" --graceful --nvpcr=unauth foo + log="$(varlinkctl call /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend.Extend '{"nvpcr":"unauth","text":"foo"}' 2>&1 || :)" + grep -F "io.systemd.PCRExtend.NoSuchNvPCR" <<<"$log" >/dev/null + + # In the initrd, systemd-pcrextend attempts to lazily initialize the NvPCR, which must fail in the + # same way and be reported as the NvPCR not being initialized. + rc=0 + log="$(SYSTEMD_IN_INITRD=1 "$SD_PCREXTEND" --nvpcr=unauth foo 2>&1)" || rc=$? + [[ "$rc" -ne 0 ]] + grep -F "$expected" <<<"$log" >/dev/null + log="$(SYSTEMD_IN_INITRD=1 "$SD_PCREXTEND" --graceful --nvpcr=unauth foo 2>&1)" + grep -F "$expected" <<<"$log" >/dev/null + grep -F "NvPCR 'unauth' is not initialized, skipping gracefully." <<<"$log" >/dev/null + log="$(SYSTEMD_IN_INITRD=1 varlinkctl call /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend.Extend '{"nvpcr":"unauth","text":"foo"}' 2>&1 || :)" + grep -F "$expected" <<<"$log" >/dev/null + grep -F "io.systemd.PCRExtend.NoSuchNvPCR" <<<"$log" >/dev/null + test ! -f /run/systemd/nvpcr/unauth.auth + (! tpm2_nvreadpublic 0x01d10216) +} + +# No PCR public key +mv /run/systemd/tpm2-pcr-public-key.pem /tmp/tpm2-pcr-public-key.pem +check_nvpcr_unauth_skipped "No PCR public key available" +mv /tmp/tpm2-pcr-public-key.pem /run/systemd/tpm2-pcr-public-key.pem + +# No PCR signature file +rm -f /run/systemd/tpm2-pcr-signature.json +check_nvpcr_unauth_skipped "No signed PCR policy available" + +# PCR signature without a policy with the "initrd" reference +"$SD_MEASURE" sign --current --bank sha256 --private-key="/tmp/tpm2-pcr-private-key.pem" --public-key="/run/systemd/tpm2-pcr-public-key.pem" --phase=":" --policyref="foo" >"/run/systemd/tpm2-pcr-signature.json" +check_nvpcr_unauth_skipped "No signed PCR policy with reference 'initrd' available" +"$SD_MEASURE" sign --current --bank sha256 --private-key="/tmp/tpm2-pcr-private-key.pem" --public-key="/run/systemd/tpm2-pcr-public-key.pem" --phase=":" >"/run/systemd/tpm2-pcr-signature.json" +check_nvpcr_unauth_skipped "No signed PCR policy with reference 'initrd' available" + +# Outside the initrd, NvPCR initialization must be skipped even with a valid PCR public key and signed +# PCR policy, as the policy can no longer be satisfied once the initrd has been left. This happens if +# systemd-tpm2-setup-early.service is not included in the initrd. systemd-pcrextend doesn't attempt +# lazy initialization outside the initrd either, so the NvPCR must be treated as not initialized. +cp /tmp/tpm2-pcr-signature.json.valid /run/systemd/tpm2-pcr-signature.json +rc=0 +SETUP_LOG="$(SYSTEMD_IN_INITRD=0 "$SD_TPM2SETUP" --early=yes 2>&1)" || rc=$? +[[ "$rc" -eq 78 ]] +grep -F "NvPCR 'unauth' is not initialized yet and initialization is only available in the initrd, refusing." <<<"$SETUP_LOG" >/dev/null +grep -F "Skipped 2 NvPCR(s) because NvPCR initialization is not authorized in this boot" <<<"$SETUP_LOG" >/dev/null +grep -F "skipping allocation of NvPCR 'unauth2'." <<<"$SETUP_LOG" >/dev/null +(! grep -F "NvPCR 'unauth2' is not initialized yet" <<<"$SETUP_LOG" >/dev/null) +test ! -f /run/systemd/nvpcr/unauth.auth +test ! -f /run/systemd/nvpcr/unauth2.auth +(! tpm2_nvreadpublic 0x01d10216) +(! tpm2_nvreadpublic 0x01d10217) +(! SYSTEMD_IN_INITRD=0 "$SD_PCREXTEND" --nvpcr=unauth foo) +SYSTEMD_IN_INITRD=0 "$SD_PCREXTEND" --graceful --nvpcr=unauth foo +VARLINK_LOG="$(SYSTEMD_IN_INITRD=0 varlinkctl call /usr/lib/systemd/systemd-pcrextend io.systemd.PCRExtend.Extend '{"nvpcr":"unauth","text":"foo"}' 2>&1 || :)" +grep -F "io.systemd.PCRExtend.NoSuchNvPCR" <<<"$VARLINK_LOG" >/dev/null +test ! -f /run/systemd/nvpcr/unauth.auth +(! tpm2_nvreadpublic 0x01d10216) + +# A policy with the "initrd" reference but an invalid signature must still fail +jq --arg sig "$(openssl rand -base64 256 | tr -d '\n')" '.sha256[].sig = $sig' /run/systemd/tpm2-pcr-signature.json +SETUP_LOG="$(rc=0; SYSTEMD_IN_INITRD=1 $SD_TPM2SETUP --early=yes 2>&1 || rc=$?; [[ "$rc" -ne 0 ]] && [[ "$rc" -ne 69 ]] && [[ "$rc" -ne 78 ]])" +grep -F "Failed to initialize NvPCR index: State not recoverable" <<<"$SETUP_LOG" >/dev/null +test ! -f /run/systemd/nvpcr/unauth.auth +test ! -f /run/systemd/nvpcr/unauth2.auth +tpm2_nvundefine -C o 0x01d10216 +tpm2_nvundefine -C o 0x01d10217 + +# And with the valid signature restored, the NvPCRs get initialized +cp /tmp/tpm2-pcr-signature.json.valid /run/systemd/tpm2-pcr-signature.json +run_tpm2_setup +test -f /run/systemd/nvpcr/unauth.auth +test -f /run/systemd/nvpcr/unauth2.auth +"$SD_PCREXTEND" --nvpcr=unauth foo +"$SD_PCREXTEND" --nvpcr=unauth2 foo +rm -f /run/nvpcr/unauth.nvpcr /run/nvpcr/unauth2.nvpcr /run/systemd/nvpcr/unauth.auth /run/systemd/nvpcr/unauth2.auth +tpm2_nvundefine -C o 0x01d10216 +tpm2_nvundefine -C o 0x01d10217 + # Verify that we can't redefine an NvPCR once we've exitted early boot by extending PCR11. rm -f /run/systemd/nvpcr/test.auth tpm2_nvundefine -C o 0x01d1020a "$SD_PCREXTEND" --pcr 11 "foo" -SETUP_LOG="$(rc=0; $SD_TPM2SETUP --early=yes 2>&1 || rc=$?; [[ "$rc" -ne 0 ]] && [[ "$rc" -ne 69 ]])" +SETUP_LOG="$(rc=0; SYSTEMD_IN_INITRD=1 $SD_TPM2SETUP --early=yes 2>&1 || rc=$?; [[ "$rc" -ne 0 ]] && [[ "$rc" -ne 69 ]] && [[ "$rc" -ne 78 ]])" grep -F "Failed to initialize NvPCR index: Device not a stream" <<<"$SETUP_LOG" >/dev/null # Test the --login= mode and the 'login' NvPCR, used in production by systemd-pcrlogin@.service. diff --git a/units/systemd-tpm2-setup-early.service.in b/units/systemd-tpm2-setup-early.service.in index bafb675196..c75b277431 100644 --- a/units/systemd-tpm2-setup-early.service.in +++ b/units/systemd-tpm2-setup-early.service.in @@ -28,3 +28,6 @@ SuccessExitStatus=PROTOCOL SuccessExitStatus=UNAVAILABLE # The tool returns EX_CANTCREAT if the NV index space is exhausted. SuccessExitStatus=CANTCREAT +# The tool returns EX_CONFIG if NvPCR initialization is not authorized in this boot (no PCR public key, no signed +# policy for NvPCR initialization, or not running in the initrd). +SuccessExitStatus=CONFIG diff --git a/units/systemd-tpm2-setup.service.in b/units/systemd-tpm2-setup.service.in index 4593211c1e..f0489777b3 100644 --- a/units/systemd-tpm2-setup.service.in +++ b/units/systemd-tpm2-setup.service.in @@ -27,5 +27,3 @@ ExecStart={{LIBEXECDIR}}/systemd-tpm2-setup --graceful SuccessExitStatus=PROTOCOL # The tool returns EX_UNAVAILABLE if some key functionality (e.g. NvPCR) support is not available in the TPM device. SuccessExitStatus=UNAVAILABLE -# The tool returns EX_CANTCREAT if the NV index space is exhausted. -SuccessExitStatus=CANTCREAT