From 9d50c30606ce9f04120c67fa1cdbe497ecb393ce Mon Sep 17 00:00:00 2001 From: Dan Streetman Date: Fri, 21 Jul 2023 15:49:16 -0400 Subject: [PATCH] cryptenroll: add support for calculated TPM2 enrollment Instead of enrolling the local TPM to a luks volume, use the public key from a TPM to enroll it into the luks volume. This is useful when enrolling a TPM that is not currently accessible, for example if the TPM is located on a different system. (cherry picked from commit c3a2a681bed77ce4f9218cd28405994ab5263077) Resolves: RHEL-16182 --- man/systemd-cryptenroll.xml | 20 +++++++ src/cryptenroll/cryptenroll-tpm2.c | 84 +++++++++++++++++++++++------- src/cryptenroll/cryptenroll-tpm2.h | 4 +- src/cryptenroll/cryptenroll.c | 21 +++++++- src/shared/tpm2-util.c | 2 +- src/shared/tpm2-util.h | 2 + 6 files changed, 111 insertions(+), 22 deletions(-) diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index 979e57d126..36c59cd7c9 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -375,6 +375,26 @@ enrollment to. + + PATH + + Enroll a TPM2 security chip using its public key. Expects a path referring to the + TPM2 public key in TPM2B_PUBLIC format. This cannot be used with , as + it performs the same operation, but without connecting to the TPM2 security chip; instead the + enrollment is calculated using the provided TPM2 key. This is useful in situations where the TPM2 + security chip is not available at the time of enrollment. + + The key, in most cases, should be the Storage Root Key (SRK) from the TPM2 security chip. If a + key from a different handle (not the SRK) is used, you must specify its handle index using + . + + You may use tpm2-tss tools to get the SRK from the TPM2 security chip with tpm2_readpublic1, + for example: + + tpm2_readpublic -c 0x81000001 -o srk.pub + + HANDLE diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 631aeea3b5..6356b1317a 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -134,6 +134,7 @@ int enroll_tpm2(struct crypt_device *cd, size_t volume_key_size, const char *device, uint32_t seal_key_handle, + const char *device_key, Tpm2PCRValue *hash_pcr_values, size_t n_hash_pcr_values, const char *pubkey_path, @@ -207,16 +208,52 @@ int enroll_tpm2(struct crypt_device *cd, return log_debug_errno(r, "Failed to read TPM PCR signature: %m"); } + bool any_pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values); + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; - r = tpm2_context_new(device, &tpm2_context); - if (r < 0) - return log_error_errno(r, "Failed to create TPM2 context: %m"); + TPM2B_PUBLIC device_key_public = {}; + if (device_key) { + _cleanup_free_ char *device_key_buffer = NULL; + size_t device_key_buffer_size; + r = read_full_file(device_key, &device_key_buffer, &device_key_buffer_size); + if (r < 0) + return log_error_errno(r, "Failed to read device key from file: %m"); - bool pcr_value_specified = tpm2_pcr_values_has_any_values(hash_pcr_values, n_hash_pcr_values); + r = dlopen_tpm2(); + if (r < 0) + return log_debug_errno(r, "TPM2 support not installed: %m"); + + TSS2_RC rc; + size_t offset = 0; + rc = sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal( + (uint8_t*) device_key_buffer, + device_key_buffer_size, + &offset, + &device_key_public); + if (rc != TSS2_RC_SUCCESS) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Could not unmarshal public key from file."); - r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values); - if (r < 0) - return log_error_errno(r, "Could not read pcr values: %m"); + assert(offset <= device_key_buffer_size); + if (offset != device_key_buffer_size) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Found %zu bytes of trailing garbage in public key file.", + device_key_buffer_size - offset); + + if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Must provide all PCR values when using TPM2 device key."); + } else { + r = tpm2_context_new(device, &tpm2_context); + if (r < 0) + return log_error_errno(r, "Failed to create TPM2 context: %m"); + + if (!tpm2_pcr_values_has_all_values(hash_pcr_values, n_hash_pcr_values)) { + r = tpm2_pcr_read_missing_values(tpm2_context, hash_pcr_values, n_hash_pcr_values); + if (r < 0) + return log_error_errno(r, "Could not read pcr values: %m"); + } + } uint16_t hash_pcr_bank = 0; uint32_t hash_pcr_mask = 0; @@ -252,15 +289,26 @@ int enroll_tpm2(struct crypt_device *cd, if (r < 0) return r; - r = tpm2_seal(tpm2_context, - seal_key_handle, - &policy, - pin_str, - &secret, &secret_size, - &blob, &blob_size, - /* ret_primary_alg= */ NULL, - &srk_buf, - &srk_buf_size); + if (device_key) + r = tpm2_calculate_seal( + seal_key_handle, + &device_key_public, + /* attributes= */ NULL, + /* secret= */ NULL, /* secret_size= */ 0, + &policy, + pin_str, + &secret, &secret_size, + &blob, &blob_size, + &srk_buf, &srk_buf_size); + else + r = tpm2_seal(tpm2_context, + seal_key_handle, + &policy, + pin_str, + &secret, &secret_size, + &blob, &blob_size, + /* ret_primary_alg= */ NULL, + &srk_buf, &srk_buf_size); if (r < 0) return log_error_errno(r, "Failed to seal to TPM2: %m"); @@ -275,8 +323,8 @@ int enroll_tpm2(struct crypt_device *cd, return r; /* return existing keyslot, so that wiping won't kill it */ } - /* Quick verification that everything is in order, we are not in a hurry after all. */ - if ((!pubkey || signature_json) && !pcr_value_specified) { + /* If possible, verify the sealed data object. */ + if ((!pubkey || signature_json) && !any_pcr_value_specified && !device_key) { _cleanup_(erase_and_freep) void *secret2 = NULL; size_t secret2_size; diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h index 8a57bdda01..c08a19c474 100644 --- a/src/cryptenroll/cryptenroll-tpm2.h +++ b/src/cryptenroll/cryptenroll-tpm2.h @@ -8,9 +8,9 @@ #include "tpm2-util.h" #if HAVE_TPM2 -int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin); +int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin); #else -static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) { +static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t seal_key_handle, const char *device_key, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin) { return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 key enrollment not supported."); } diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index 5ace7a9787..d2fffdad24 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -34,6 +34,7 @@ static char *arg_pkcs11_token_uri = NULL; static char *arg_fido2_device = NULL; static char *arg_tpm2_device = NULL; static uint32_t arg_tpm2_seal_key_handle = 0; +static char *arg_tpm2_device_key = NULL; static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL; static size_t arg_tpm2_n_hash_pcr_values = 0; static bool arg_tpm2_hash_pcr_values_use_default = true; @@ -60,6 +61,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_unlock_keyfile, freep); STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, freep); STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); +STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep); @@ -124,6 +126,8 @@ static int help(void) { " Enroll a TPM2 device\n" " --tpm2-seal-key-handle=HANDLE\n" " Specify handle of key to use for sealing\n" + " --tpm2-device-key=PATH\n" + " Enroll a TPM2 device using its public key\n" " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n" " Specify TPM2 PCRs to seal against\n" " --tpm2-public-key=PATH\n" @@ -157,6 +161,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_FIDO2_DEVICE, ARG_TPM2_DEVICE, ARG_TPM2_SEAL_KEY_HANDLE, + ARG_TPM2_DEVICE_KEY, ARG_TPM2_PCRS, ARG_TPM2_PUBLIC_KEY, ARG_TPM2_PUBLIC_KEY_PCRS, @@ -183,6 +188,7 @@ static int parse_argv(int argc, char *argv[]) { { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-seal-key-handle", required_argument, NULL, ARG_TPM2_SEAL_KEY_HANDLE }, + { "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY }, { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS }, { "tpm2-public-key", required_argument, NULL, ARG_TPM2_PUBLIC_KEY }, { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS }, @@ -349,6 +355,19 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_TPM2_DEVICE_KEY: + if (arg_enroll_type >= 0 || arg_tpm2_device_key) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Multiple operations specified at once, refusing."); + + + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_device_key); + if (r < 0) + return r; + + arg_enroll_type = ENROLL_TPM2; + break; + case ARG_TPM2_PCRS: arg_tpm2_hash_pcr_values_use_default = false; r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values); @@ -679,7 +698,7 @@ static int run(int argc, char *argv[]) { break; case ENROLL_TPM2: - slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin); + slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_seal_key_handle, arg_tpm2_device_key, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, arg_tpm2_public_key, arg_tpm2_public_key_pcr_mask, arg_tpm2_signature, arg_tpm2_pin); break; case _ENROLL_TYPE_INVALID: diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index e334564159..4e382f691e 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -81,7 +81,7 @@ static TSS2_RC (*sym_Tss2_MU_TPM2B_NAME_Marshal)(TPM2B_NAME const *src, uint8_t static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL; static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL; static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL; -static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL; +TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL; static TSS2_RC (*sym_Tss2_MU_TPM2B_SENSITIVE_Marshal)(TPM2B_SENSITIVE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL; static TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL; static TSS2_RC (*sym_Tss2_MU_TPMS_NV_PUBLIC_Marshal)(TPMS_NV_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index 34d4610383..380e96ec83 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -263,6 +263,8 @@ int tpm2_tpm2b_public_to_fingerprint(const TPM2B_PUBLIC *public, void **ret_fing 0; \ }) +extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest); + #else /* HAVE_TPM2 */ typedef struct {} Tpm2Context; typedef struct {} Tpm2Handle;