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.
This commit is contained in:
Lennart Poettering 2023-10-24 22:30:47 +02:00
parent a434270139
commit 404aea7815
10 changed files with 116 additions and 14 deletions

View file

@ -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,

View file

@ -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.");
}

View file

@ -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:

View file

@ -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));
}

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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(