systemd/0641-cryptenroll-add-suppor...

289 lines
16 KiB
Diff

From 9d50c30606ce9f04120c67fa1cdbe497ecb393ce Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
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.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--tpm2-device-key=</option><replaceable>PATH</replaceable></term>
+
+ <listitem><para>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 <option>--tpm2-device=</option>, 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.</para>
+
+ <para>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
+ <option>--tpm2-seal-key-handle=</option>.</para>
+
+ <para>You may use tpm2-tss tools to get the SRK from the TPM2 security chip with <citerefentry
+ project='mankier'><refentrytitle>tpm2_readpublic</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ for example:</para>
+
+ <programlisting>tpm2_readpublic -c 0x81000001 -o srk.pub</programlisting></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--tpm2-seal-key-handle=</option><replaceable>HANDLE</replaceable></term>
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;