diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 49e2353d31..213691f0d4 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -1492,6 +1492,59 @@ static int tpm2_make_encryption_session( return 0; } +static int tpm2_make_policy_session( + Tpm2Context *c, + const Tpm2Handle *primary, + const Tpm2Handle *encryption_session, + bool trial, + Tpm2Handle **ret_session) { + + static const TPMT_SYM_DEF symmetric = { + .algorithm = TPM2_ALG_AES, + .keyBits.aes = 128, + .mode.aes = TPM2_ALG_CFB, + }; + TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY; + TSS2_RC rc; + int r; + + assert(c); + assert(primary); + assert(encryption_session); + assert(ret_session); + + if (!tpm2_is_encryption_session(c, encryption_session)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Missing encryption session"); + + log_debug("Starting policy session."); + + _cleanup_tpm2_handle_ Tpm2Handle *session = NULL; + r = tpm2_handle_new(c, &session); + if (r < 0) + return r; + + rc = sym_Esys_StartAuthSession( + c->esys_context, + primary->esys_handle, + ESYS_TR_NONE, + encryption_session->esys_handle, + ESYS_TR_NONE, + ESYS_TR_NONE, + NULL, + session_type, + &symmetric, + TPM2_ALG_SHA256, + &session->esys_handle); + if (rc != TSS2_RC_SUCCESS) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc)); + + *ret_session = TAKE_PTR(session); + + return 0; +} + static int openssl_pubkey_to_tpm2_pubkey( const void *pubkey, size_t pubkey_size, @@ -1709,87 +1762,36 @@ static int find_signature( #endif } -static int tpm2_make_policy_session( +static int tpm2_build_sealing_policy( Tpm2Context *c, - const Tpm2Handle *primary, - const Tpm2Handle *parent_session, - TPM2_SE session_type, + const Tpm2Handle *session, uint32_t hash_pcr_mask, - uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */ + uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature_json, bool use_pin, - Tpm2Handle **ret_session, - TPM2B_DIGEST **ret_policy_digest, - TPMI_ALG_HASH *ret_pcr_bank) { + TPM2B_DIGEST **ret_policy_digest) { - static const TPMT_SYM_DEF symmetric = { - .algorithm = TPM2_ALG_AES, - .keyBits.aes = 128, - .mode.aes = TPM2_ALG_CFB, - }; TSS2_RC rc; int r; assert(c); + assert(session); assert(pubkey || pubkey_size == 0); assert(pubkey_pcr_mask == 0 || pubkey_size > 0); - log_debug("Starting authentication session."); - - /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's - * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR - * policies (since only then we need to shove PCR values into the policy that don't match current - * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in - * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is - * non-zero, i.e. signed PCR policy is requested. - * - * One day we should switch to calculating policy hashes client side when trial mode is requested, to - * avoid this mess. */ - if (session_type == TPM2_SE_TRIAL && pubkey_pcr_mask == 0) - session_type = TPM2_SE_POLICY; + log_debug("Building sealing policy."); if ((hash_pcr_mask | pubkey_pcr_mask) != 0) { - /* We are told to configure a PCR policy of some form, let's determine/validate the PCR bank to use. */ - - if (pcr_bank != UINT16_MAX) { - r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask); - if (r < 0) - return r; - if (r == 0) - log_warning("Selected TPM2 PCRs are not initialized on this system, most likely due to a firmware issue. PCR policy is effectively not enforced. Proceeding anyway."); - } else { - /* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we - * detect that use that, but preferably use SHA256 */ - r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank); - if (r < 0) - return r; - } + r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask); + if (r < 0) + return r; + if (r == 0) + log_warning("Selected TPM2 PCRs are not initialized on this system."); } - _cleanup_tpm2_handle_ Tpm2Handle *session = NULL; - r = tpm2_handle_new(c, &session); - if (r < 0) - return r; - - rc = sym_Esys_StartAuthSession( - c->esys_context, - primary->esys_handle, - ESYS_TR_NONE, - parent_session->esys_handle, - ESYS_TR_NONE, - ESYS_TR_NONE, - NULL, - session_type, - &symmetric, - TPM2_ALG_SHA256, - &session->esys_handle); - if (rc != TSS2_RC_SUCCESS) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc)); - if (pubkey_pcr_mask != 0) { _cleanup_free_ void *fp = NULL; size_t fp_size = 0; @@ -1970,12 +1972,6 @@ static int tpm2_make_policy_session( if (r < 0) return r; - if (ret_session) - *ret_session = TAKE_PTR(session); - - if (ret_pcr_bank) - *ret_pcr_bank = pcr_bank; - return 0; } @@ -2048,33 +2044,57 @@ int tpm2_seal(const char *device, if (r < 0) return r; + TPMI_ALG_HASH pcr_bank = 0; + if (hash_pcr_mask | pubkey_pcr_mask) { + /* Some TPM2 devices only can do SHA1. Prefer SHA256 but allow SHA1. */ + r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank); + if (r < 0) + return r; + } + _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL; r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary); if (r < 0) return r; /* we cannot use the bind key before its created */ - _cleanup_tpm2_handle_ Tpm2Handle *session = NULL; - r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &session); + _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL; + r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &encryption_session); + if (r < 0) + return r; + + /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's + * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR + * policies (since only then we need to shove PCR values into the policy that don't match current + * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in + * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is + * non-zero, i.e. signed PCR policy is requested. + * + * One day we should switch to calculating policy hashes client side when trial mode is requested, to + * avoid this mess. */ + bool trial = (pubkey_pcr_mask != 0); + + _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL; + r = tpm2_make_policy_session( + c, + primary, + encryption_session, + trial, + &policy_session); if (r < 0) return r; _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; - TPMI_ALG_HASH pcr_bank; - r = tpm2_make_policy_session( + r = tpm2_build_sealing_policy( c, - primary, - session, - TPM2_SE_TRIAL, + policy_session, hash_pcr_mask, - /* pcr_bank= */ UINT16_MAX, + pcr_bank, pubkey, pubkey_size, pubkey_pcr_mask, /* signature_json= */ NULL, !!pin, - /* ret_session= */ NULL, - &policy_digest, - &pcr_bank); + &policy_digest); if (r < 0) return r; @@ -2115,7 +2135,7 @@ int tpm2_seal(const char *device, rc = sym_Esys_Create( c->esys_context, primary->esys_handle, - session->esys_handle, /* use HMAC session to enable parameter encryption */ + encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */ ESYS_TR_NONE, ESYS_TR_NONE, &hmac_sensitive, @@ -2333,8 +2353,8 @@ int tpm2_unseal(const char *device, sym_Tss2_RC_Decode(rc)); } - _cleanup_tpm2_handle_ Tpm2Handle *hmac_session = NULL; - r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &hmac_session); + _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL; + r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &encryption_session); if (r < 0) return r; @@ -2344,17 +2364,22 @@ int tpm2_unseal(const char *device, r = tpm2_make_policy_session( c, primary, - hmac_session, - TPM2_SE_POLICY, + encryption_session, + /* trial= */ false, + &policy_session); + if (r < 0) + return r; + + r = tpm2_build_sealing_policy( + c, + policy_session, hash_pcr_mask, pcr_bank, pubkey, pubkey_size, pubkey_pcr_mask, signature, !!pin, - &policy_session, - &policy_digest, - /* ret_pcr_bank= */ NULL); + &policy_digest); if (r < 0) return r; @@ -2372,7 +2397,7 @@ int tpm2_unseal(const char *device, c->esys_context, hmac_key->esys_handle, policy_session->esys_handle, - hmac_session->esys_handle, /* use HMAC session to enable parameter encryption */ + encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */ ESYS_TR_NONE, &unsealed); if (rc == TSS2_RC_SUCCESS)