summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--man/crypttab.xml15
-rw-r--r--src/cryptsetup/cryptsetup-tpm2.c62
-rw-r--r--src/cryptsetup/cryptsetup-tpm2.h22
-rw-r--r--src/cryptsetup/cryptsetup.c34
4 files changed, 108 insertions, 25 deletions
diff --git a/man/crypttab.xml b/man/crypttab.xml
index 0469d365ef..2a54c81595 100644
--- a/man/crypttab.xml
+++ b/man/crypttab.xml
@@ -686,6 +686,21 @@
</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>
<listitem><para>Specifies how long to wait at most for configured security devices (i.e. FIDO2,
diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c
index c348e73b21..7469e7da1b 100644
--- a/src/cryptsetup/cryptsetup-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tpm2.c
@@ -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;
diff --git a/src/cryptsetup/cryptsetup-tpm2.h b/src/cryptsetup/cryptsetup-tpm2.h
index ab16d0a18f..b1bcf6de10 100644
--- a/src/cryptsetup/cryptsetup-tpm2.h
+++ b/src/cryptsetup/cryptsetup-tpm2.h
@@ -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,
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 90bce953de..c419354bf5 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -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,