From: Lennart Poettering Date: Tue, 24 Oct 2023 20:30:47 +0000 (+0200) Subject: tree-wide: hook everything up with pcrlock policy X-Git-Tag: v255-rc1~27^2~5 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=404aea7815595c1324947ed7f2a7502b17d3cc01;p=thirdparty%2Fsystemd.git tree-wide: hook everything up with pcrlock policy Make sure cryptenroll and repart can enroll TPM2 policies with pcrlock logic. Make sure cryptsetup can unlock TPM2 policies with pcrlock in effect. --- diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index c0a19bfe929..1273822d3ae 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -139,7 +139,8 @@ int enroll_tpm2(struct crypt_device *cd, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, - bool use_pin) { + bool use_pin, + const char *pcrlock_path) { _cleanup_(erase_and_freep) void *secret = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL; @@ -207,6 +208,15 @@ int enroll_tpm2(struct crypt_device *cd, return log_debug_errno(r, "Failed to read TPM PCR signature: %m"); } + _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {}; + if (pcrlock_path) { + r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy); + if (r < 0) + return r; + + flags |= TPM2_FLAGS_USE_PCRLOCK; + } + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; r = tpm2_context_new(device, &tpm2_context); if (r < 0) @@ -248,7 +258,7 @@ int enroll_tpm2(struct crypt_device *cd, n_hash_pcr_values, pubkey ? &public : NULL, use_pin, - /* pcrlock_policy= */ NULL, + pcrlock_path ? &pcrlock_policy : NULL, &policy); if (r < 0) return r; @@ -289,7 +299,7 @@ int enroll_tpm2(struct crypt_device *cd, pubkey_pcr_mask, signature_json, pin_str, - /* pcrlock_policy= */ NULL, + pcrlock_path ? &pcrlock_policy : NULL, /* primary_alg= */ 0, blob, blob_size, policy.buffer, policy.size, diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h index 8a57bdda015..6439a08df40 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, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path); #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, Tpm2PCRValue *hash_pcrs, size_t n_hash_pcrs, const char *pubkey_path, uint32_t pubkey_pcr_mask, const char *signature_path, bool use_pin, const char *pcrlock_path) { 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 8b65485a294..20d6bc26809 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -43,6 +43,7 @@ static bool arg_tpm2_pin = false; static char *arg_tpm2_public_key = NULL; static uint32_t arg_tpm2_public_key_pcr_mask = 0; static char *arg_tpm2_signature = NULL; +static char *arg_tpm2_pcrlock = NULL; static char *arg_node = NULL; static int *arg_wipe_slots = NULL; static size_t arg_n_wipe_slots = 0; @@ -65,6 +66,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, 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); +STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep); STATIC_DESTRUCTOR_REGISTER(arg_node, freep); STATIC_DESTRUCTOR_REGISTER(arg_wipe_slots, freep); @@ -144,6 +146,8 @@ static int help(void) { " --tpm2-signature=PATH\n" " Validate public key enrollment works with JSON signature\n" " file\n" + " --tpm2-pcrlock=PATH\n" + " Specify pcrlock policy to lock against\n" " --tpm2-with-pin=BOOL\n" " Whether to require entering a PIN to unlock the volume\n" "\nSee the %2$s for details.\n", @@ -173,6 +177,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_TPM2_PUBLIC_KEY_PCRS, ARG_TPM2_SIGNATURE, ARG_TPM2_PIN, + ARG_TPM2_PCRLOCK, ARG_WIPE_SLOT, ARG_FIDO2_WITH_PIN, ARG_FIDO2_WITH_UP, @@ -200,11 +205,12 @@ static int parse_argv(int argc, char *argv[]) { { "tpm2-public-key-pcrs", required_argument, NULL, ARG_TPM2_PUBLIC_KEY_PCRS }, { "tpm2-signature", required_argument, NULL, ARG_TPM2_SIGNATURE }, { "tpm2-with-pin", required_argument, NULL, ARG_TPM2_PIN }, + { "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK }, { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT }, {} }; - bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true; + bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true; int c, r; assert(argc >= 0); @@ -412,6 +418,14 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_TPM2_PCRLOCK: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock); + if (r < 0) + return r; + + auto_pcrlock = false; + break; + case ARG_WIPE_SLOT: { const char *p = optarg; @@ -500,12 +514,23 @@ static int parse_argv(int argc, char *argv[]) { } } + if (auto_pcrlock) { + assert(!arg_tpm2_pcrlock); + + r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m"); + } else + log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock); + } + if (auto_public_key_pcr_mask) { assert(arg_tpm2_public_key_pcr_mask == 0); arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT); } - if (auto_hash_pcr_values) { + if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 by default if no pcrlock policy is around (which is a better replacement) */ assert(arg_tpm2_n_hash_pcr_values == 0); if (!GREEDY_REALLOC_APPEND( @@ -690,7 +715,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_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, arg_tpm2_pcrlock); break; case _ENROLL_TYPE_INVALID: diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index 94d568c17f6..6da80896cd5 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -109,6 +109,7 @@ _public_ int cryptsetup_token_open_pin( pubkey_pcr_mask, params.signature_path, pin_string, + params.pcrlock_path, primary_alg, blob, blob_size, @@ -239,6 +240,7 @@ _public_ void cryptsetup_token_dump( crypt_log(cd, "\ttpm2-blob: %s\n", blob_str); crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str); crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN)); + crypt_log(cd, "\ttpm2-pcrlock: %s\n", true_false(flags & TPM2_FLAGS_USE_PCRLOCK)); crypt_log(cd, "\ttpm2-salt: %s\n", true_false(salt)); crypt_log(cd, "\ttpm2-srk: %s\n", true_false(srk_buf)); } diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c index 3e86845d0d6..72be5cc71d3 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c @@ -22,6 +22,7 @@ int acquire_luks2_key( uint32_t pubkey_pcr_mask, const char *signature_path, const char *pin, + const char *pcrlock_path, uint16_t primary_alg, const void *key_data, size_t key_data_size, @@ -76,6 +77,13 @@ int acquire_luks2_key( return log_error_errno(r, "Failed to load PCR signature: %m"); } + _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {}; + if (FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK)) { + r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy); + if (r < 0) + return r; + } + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; r = tpm2_context_new(device, &tpm2_context); if (r < 0) @@ -88,7 +96,7 @@ int acquire_luks2_key( pubkey_pcr_mask, signature_json, pin, - /* pcrlock_policy= */ NULL, + FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL, primary_alg, key_data, key_data_size, policy_hash, policy_hash_size, diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h index 1143f5fd9f5..d84e5a3c3ba 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h @@ -14,6 +14,7 @@ int acquire_luks2_key( size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *signature_path, + const char *pcrlock_path, const char *pin, uint16_t primary_alg, const void *key_data, diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c index b587807ee58..f59d5f9d1dc 100644 --- a/src/cryptsetup/cryptsetup-tpm2.c +++ b/src/cryptsetup/cryptsetup-tpm2.c @@ -62,6 +62,7 @@ int acquire_tpm2_key( size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *signature_path, + const char *pcrlock_path, uint16_t primary_alg, const char *key_file, size_t key_file_size, @@ -129,6 +130,14 @@ int acquire_tpm2_key( return log_error_errno(r, "Failed to load pcr signature: %m"); } + _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {}; + + if (FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK)) { + r = tpm2_pcrlock_policy_load(pcrlock_path, &pcrlock_policy); + if (r < 0) + return r; + } + _cleanup_(tpm2_context_unrefp) Tpm2Context *tpm2_context = NULL; r = tpm2_context_new(device, &tpm2_context); if (r < 0) @@ -142,7 +151,7 @@ int acquire_tpm2_key( pubkey_pcr_mask, signature_json, /* pin= */ NULL, - /* pcrlock_policy= */ NULL, + FLAGS_SET(flags, TPM2_FLAGS_USE_PCRLOCK) ? &pcrlock_policy : NULL, primary_alg, blob, blob_size, @@ -190,7 +199,7 @@ int acquire_tpm2_key( pubkey_pcr_mask, signature_json, b64_salted_pin, - /* pcrlock_policy= */ NULL, + pcrlock_path ? &pcrlock_policy : NULL, primary_alg, blob, blob_size, diff --git a/src/cryptsetup/cryptsetup-tpm2.h b/src/cryptsetup/cryptsetup-tpm2.h index a510ac62570..a50a9435a98 100644 --- a/src/cryptsetup/cryptsetup-tpm2.h +++ b/src/cryptsetup/cryptsetup-tpm2.h @@ -20,6 +20,7 @@ int acquire_tpm2_key( size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *signature_path, + const char *pcrlock_path, uint16_t primary_alg, const char *key_file, size_t key_file_size, @@ -72,6 +73,7 @@ static inline int acquire_tpm2_key( size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *signature_path, + const char *pcrlock_path, uint16_t primary_alg, const char *key_file, size_t key_file_size, diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index a0f93178f73..3962fcede5b 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -100,6 +100,7 @@ static bool arg_tpm2_device_auto = false; static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; static char *arg_tpm2_signature = NULL; static bool arg_tpm2_pin = false; +static char *arg_tpm2_pcrlock = NULL; static bool arg_headless = false; static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC; static unsigned arg_tpm2_measure_pcr = UINT_MAX; /* This and the following field is about measuring the unlocked volume key to the local TPM */ @@ -116,6 +117,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_fido2_rp_id, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_banks, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep); static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = { [PASSPHRASE_REGULAR] = "passphrase", @@ -424,6 +426,16 @@ static int parse_one_option(const char *option) { arg_tpm2_pin = r; + } else if ((val = startswith(option, "tpm2-pcrlock="))) { + + if (!path_is_absolute(val)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "TPM2 pcrlock policy path \"%s\" is not absolute, refusing.", val); + + r = free_and_strdup(&arg_tpm2_pcrlock, val); + if (r < 0) + return log_oom(); + } else if ((val = startswith(option, "tpm2-measure-pcr="))) { unsigned pcr; @@ -1590,6 +1602,7 @@ static int attach_luks2_by_tpm2_via_plugin( .search_pcr_mask = arg_tpm2_pcr_mask, .device = arg_tpm2_device, .signature_path = arg_tpm2_signature, + .pcrlock_path = arg_tpm2_pcrlock, }; if (!libcryptsetup_plugins_support()) @@ -1649,6 +1662,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( /* pubkey= */ NULL, /* pubkey_size= */ 0, /* pubkey_pcr_mask= */ 0, /* signature_path= */ NULL, + /* pcrlock_path= */ NULL, /* primary_alg= */ 0, key_file, arg_keyfile_size, arg_keyfile_offset, key_data, key_data_size, @@ -1746,6 +1760,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( pubkey, pubkey_size, pubkey_pcr_mask, arg_tpm2_signature, + arg_tpm2_pcrlock, primary_alg, /* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */ blob, blob_size, diff --git a/src/partition/repart.c b/src/partition/repart.c index bff311c5223..01cfc46f1ce 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -152,6 +152,7 @@ static Tpm2PCRValue *arg_tpm2_hash_pcr_values = NULL; static size_t arg_tpm2_n_hash_pcr_values = 0; static char *arg_tpm2_public_key = NULL; static uint32_t arg_tpm2_public_key_pcr_mask = 0; +static char *arg_tpm2_pcrlock = NULL; static bool arg_split = false; static GptPartitionType *arg_filter_partitions = NULL; static size_t arg_n_filter_partitions = 0; @@ -175,6 +176,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep); +STATIC_DESTRUCTOR_REGISTER(arg_tpm2_pcrlock, freep); STATIC_DESTRUCTOR_REGISTER(arg_filter_partitions, freep); STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep); STATIC_DESTRUCTOR_REGISTER(arg_copy_from, strv_freep); @@ -3821,13 +3823,20 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta return log_error_errno(r, "Could not get hash mask: %m"); } + _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy pcrlock_policy = {}; + if (arg_tpm2_pcrlock) { + r = tpm2_pcrlock_policy_load(arg_tpm2_pcrlock, &pcrlock_policy); + if (r < 0) + return r; + } + TPM2B_DIGEST policy = TPM2B_DIGEST_MAKE(NULL, TPM2_SHA256_DIGEST_SIZE); r = tpm2_calculate_sealing_policy( arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, pubkey ? &public : NULL, /* use_pin= */ false, - /* pcrlock_policy= */ NULL, + arg_tpm2_pcrlock ? &pcrlock_policy : NULL, &policy); if (r < 0) return log_error_errno(r, "Could not calculate sealing policy digest: %m"); @@ -6441,6 +6450,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_TPM2_PCRS, ARG_TPM2_PUBLIC_KEY, ARG_TPM2_PUBLIC_KEY_PCRS, + ARG_TPM2_PCRLOCK, ARG_SPLIT, ARG_INCLUDE_PARTITIONS, ARG_EXCLUDE_PARTITIONS, @@ -6478,6 +6488,7 @@ static int parse_argv(int argc, char *argv[]) { { "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 }, + { "tpm2-pcrlock", required_argument, NULL, ARG_TPM2_PCRLOCK }, { "split", required_argument, NULL, ARG_SPLIT }, { "include-partitions", required_argument, NULL, ARG_INCLUDE_PARTITIONS }, { "exclude-partitions", required_argument, NULL, ARG_EXCLUDE_PARTITIONS }, @@ -6491,7 +6502,7 @@ static int parse_argv(int argc, char *argv[]) { {} }; - bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true; + bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true; int c, r; assert(argc >= 0); @@ -6734,6 +6745,14 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_TPM2_PCRLOCK: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_tpm2_pcrlock); + if (r < 0) + return r; + + auto_pcrlock = false; + break; + case ARG_SPLIT: r = parse_boolean_argument("--split=", optarg, NULL); if (r < 0) @@ -6941,12 +6960,23 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "A path to an image file must be specified when --split is used."); + if (auto_pcrlock) { + assert(!arg_tpm2_pcrlock); + + r = tpm2_pcrlock_search_file(NULL, NULL, &arg_tpm2_pcrlock); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Search for pcrlock.json failed, assuming it does not exist: %m"); + } else + log_info("Automatically using pcrlock policy '%s'.", arg_tpm2_pcrlock); + } + if (auto_public_key_pcr_mask) { assert(arg_tpm2_public_key_pcr_mask == 0); arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT); } - if (auto_hash_pcr_values) { + if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 if no pcr policy is specified. */ assert(arg_tpm2_n_hash_pcr_values == 0); if (!GREEDY_REALLOC_APPEND(