]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: hook up signed PCR policies
authorLennart Poettering <lennart@poettering.net>
Thu, 18 Aug 2022 09:10:30 +0000 (11:10 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 8 Sep 2022 14:34:27 +0000 (16:34 +0200)
man/crypttab.xml
src/cryptsetup/cryptsetup-tpm2.c
src/cryptsetup/cryptsetup-tpm2.h
src/cryptsetup/cryptsetup.c

index 0469d365ef76869595ba90b33d48184fcac8febc..2a54c81595b1debc91168230bc2e103b8620da56 100644 (file)
         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>
 
index c348e73b2143ec4a0027605b2af034c261de080d..7469e7da1bf6b1bc439b19a268e7daecbe82d576 100644 (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;
index ab16d0a18fe83e627b42ba7ec6cd6f89657ccf77..b1bcf6de1062483b0363b1abfd858257185b1b9d 100644 (file)
 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,
index 90bce953de8cf9850a44c8378306766ed49c923e..c419354bf595abaa7ada9a16c6b474821bf7cf98 100644 (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,