cryptsetup: hook up signed PCR policies

This commit is contained in:
Lennart Poettering 2022-08-18 11:10:30 +02:00
parent 02ef97cde0
commit dc63b2c909
4 changed files with 108 additions and 25 deletions

View file

@ -685,6 +685,21 @@
when TPM2 enrollment metadata is not available.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>tpm2-signature=</option></term>
<listitem><para>Takes an absolute path to a TPM2 PCR JSON signature file, as produced by the
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
tool. This permits locking LUKS2 volumes to any PCR values for which a valid signature matching a
public key specified at key enrollment time can be provided. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details on enrolling TPM2 PCR public keys. If this option is not specified but it is attempted to
unlock a LUKS2 volume with a signed TPM2 PCR enrollment a suitable signature file
<filename>tpm2-pcr-signature.json</filename> is searched for in <filename>/etc/systemd/</filename>,
<filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this
order).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>token-timeout=</option></term>

View file

@ -57,6 +57,10 @@ int acquire_tpm2_key(
const char *device,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
const void *pubkey,
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
@ -72,6 +76,7 @@ int acquire_tpm2_key(
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
_cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
_cleanup_free_ void *loaded_blob = NULL;
_cleanup_free_ char *auto_device = NULL;
size_t blob_size;
@ -111,14 +116,20 @@ int acquire_tpm2_key(
blob = loaded_blob;
}
if (pubkey_pcr_mask != 0) {
r = tpm2_load_pcr_signature(signature_path, &signature_json);
if (r < 0)
return r;
}
if (!(flags & TPM2_FLAGS_USE_PIN))
return tpm2_unseal(
device,
hash_pcr_mask,
pcr_bank,
/* pubkey= */ NULL, /* pubkey_size= */ 0,
/* pubkey_pcr_mask= */ 0,
/* signature= */ NULL,
pubkey, pubkey_size,
pubkey_pcr_mask,
signature_json,
/* pin= */ NULL,
primary_alg,
blob,
@ -141,9 +152,9 @@ int acquire_tpm2_key(
r = tpm2_unseal(device,
hash_pcr_mask,
pcr_bank,
/* pubkey= */ NULL, /* pubkey_size= */ 0,
/* pubkey_pcr_mask= */ 0,
/* signature= */ NULL,
pubkey, pubkey_size,
pubkey_pcr_mask,
signature_json,
pin_str,
primary_alg,
blob,
@ -167,8 +178,11 @@ int find_tpm2_auto_data(
struct crypt_device *cd,
uint32_t search_pcr_mask,
int start_token,
uint32_t *ret_pcr_mask,
uint32_t *ret_hash_pcr_mask,
uint16_t *ret_pcr_bank,
void **ret_pubkey,
size_t *ret_pubkey_size,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
void **ret_blob,
size_t *ret_blob_size,
@ -178,11 +192,11 @@ int find_tpm2_auto_data(
int *ret_token,
TPM2Flags *ret_flags) {
_cleanup_free_ void *blob = NULL, *policy_hash = NULL;
size_t blob_size = 0, policy_hash_size = 0;
_cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL;
size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0;
int r, keyslot = -1, token = -1;
TPM2Flags flags = 0;
uint32_t pcr_mask = 0;
uint32_t hash_pcr_mask = 0, pubkey_pcr_mask = 0;
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
@ -212,12 +226,12 @@ int find_tpm2_auto_data(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"TPM2 token data lacks 'tpm2-pcrs' field.");
r = tpm2_parse_pcr_json_array(w, &pcr_mask);
r = tpm2_parse_pcr_json_array(w, &hash_pcr_mask);
if (r < 0)
return log_error_errno(r, "Failed to parse TPM2 PCR mask: %m");
if (search_pcr_mask != UINT32_MAX &&
search_pcr_mask != pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
search_pcr_mask != hash_pcr_mask) /* PCR mask doesn't match what is configured, ignore this entry */
continue;
assert(keyslot < 0);
@ -292,6 +306,21 @@ int find_tpm2_auto_data(
flags |= TPM2_FLAGS_USE_PIN;
}
w = json_variant_by_key(v, "tpm2_pubkey_pcrs");
if (w) {
r = tpm2_parse_pcr_json_array(w, &pubkey_pcr_mask);
if (r < 0)
return r;
}
w = json_variant_by_key(v, "tpm2_pubkey");
if (w) {
r = json_variant_unbase64(w, &pubkey, &pubkey_size);
if (r < 0)
return log_error_errno(r, "Failed to decode PCR public key.");
} else if (pubkey_pcr_mask != 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Public key PCR mask set, but not public key included in JSON data, refusing.");
break;
}
@ -302,15 +331,18 @@ int find_tpm2_auto_data(
if (start_token <= 0)
log_info("Automatically discovered security TPM2 token unlocks volume.");
*ret_pcr_mask = pcr_mask;
*ret_hash_pcr_mask = hash_pcr_mask;
*ret_pcr_bank = pcr_bank;
*ret_pubkey = TAKE_PTR(pubkey);
*ret_pubkey_size = pubkey_size;
*ret_pubkey_pcr_mask = pubkey_pcr_mask;
*ret_primary_alg = primary_alg;
*ret_blob = TAKE_PTR(blob);
*ret_blob_size = blob_size;
*ret_policy_hash = TAKE_PTR(policy_hash);
*ret_policy_hash_size = policy_hash_size;
*ret_keyslot = keyslot;
*ret_token = token;
*ret_pcr_bank = pcr_bank;
*ret_primary_alg = primary_alg;
*ret_flags = flags;
return 0;

View file

@ -14,8 +14,12 @@
int acquire_tpm2_key(
const char *volume_name,
const char *device,
uint32_t pcr_mask,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
const void *pubkey,
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
@ -35,8 +39,11 @@ int find_tpm2_auto_data(
struct crypt_device *cd,
uint32_t search_pcr_mask,
int start_token,
uint32_t *ret_pcr_mask,
uint32_t *ret_hash_pcr_mask,
uint16_t *ret_pcr_bank,
void **ret_pubkey,
size_t *ret_pubkey_size,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
void **ret_blob,
size_t *ret_blob_size,
@ -51,8 +58,12 @@ int find_tpm2_auto_data(
static inline int acquire_tpm2_key(
const char *volume_name,
const char *device,
uint32_t pcr_mask,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
const void *pubkey,
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
const char *signature_path,
uint16_t primary_alg,
const char *key_file,
size_t key_file_size,
@ -76,8 +87,11 @@ static inline int find_tpm2_auto_data(
struct crypt_device *cd,
uint32_t search_pcr_mask,
int start_token,
uint32_t *ret_pcr_mask,
uint32_t *ret_hash_pcr_mask,
uint16_t *ret_pcr_bank,
void **ret_pubkey,
size_t *ret_pubkey_size,
uint32_t *ret_pubkey_pcr_mask,
uint16_t *ret_primary_alg,
void **ret_blob,
size_t *ret_blob_size,

View file

@ -92,6 +92,7 @@ static char *arg_fido2_rp_id = NULL;
static char *arg_tpm2_device = NULL;
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 bool arg_headless = false;
static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
@ -105,6 +106,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_cid, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_rp_id, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = {
[PASSPHRASE_REGULAR] = "passphrase",
@ -398,6 +400,16 @@ static int parse_one_option(const char *option) {
if (r < 0)
return r;
} else if ((val = startswith(option, "tpm2-signature="))) {
if (!path_is_absolute(val))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"TPM2 signature path \"%s\" is not absolute, refusing.", val);
r = free_and_strdup(&arg_tpm2_signature, val);
if (r < 0)
return log_oom();
} else if ((val = startswith(option, "tpm2-pin="))) {
r = parse_boolean(val);
@ -1441,10 +1453,13 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
arg_tpm2_device,
arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask,
UINT16_MAX,
0,
/* pubkey= */ NULL, /* pubkey_size= */ 0,
/* pubkey_pcr_mask= */ 0,
/* signature_path= */ NULL,
/* primary_alg= */ 0,
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
NULL, 0, /* we don't know the policy hash */
/* policy_hash= */ NULL, /* policy_hash_size= */ 0, /* we don't know the policy hash */
arg_tpm2_pin,
until,
arg_headless,
@ -1490,7 +1505,9 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
* works. */
for (;;) {
uint32_t pcr_mask;
_cleanup_free_ void *pubkey = NULL;
size_t pubkey_size = 0;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
TPM2Flags tpm2_flags;
@ -1498,8 +1515,10 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
cd,
arg_tpm2_pcr_mask, /* if != UINT32_MAX we'll only look for tokens with this PCR mask */
token, /* search for the token with this index, or any later index than this */
&pcr_mask,
&hash_pcr_mask,
&pcr_bank,
&pubkey, &pubkey_size,
&pubkey_pcr_mask,
&primary_alg,
&blob, &blob_size,
&policy_hash, &policy_hash_size,
@ -1523,10 +1542,13 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
r = acquire_tpm2_key(
name,
arg_tpm2_device,
pcr_mask,
hash_pcr_mask,
pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
arg_tpm2_signature,
primary_alg,
NULL, 0, 0, /* no key file */
/* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
blob, blob_size,
policy_hash, policy_hash_size,
tpm2_flags,