]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cryptsetup: hook up TPM2 token code with policies based on PCR signatures, too
authorLennart Poettering <lennart@poettering.net>
Fri, 19 Aug 2022 20:18:40 +0000 (22:18 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 8 Sep 2022 14:34:27 +0000 (16:34 +0200)
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
src/cryptsetup/cryptsetup.c
src/shared/tpm2-util.h

index bbc8a39c98b7d37e0caebbaf48ba9ab0744d0a6c..7e958a4db580be7d57c2255a748b87545c448ded 100644 (file)
@@ -40,27 +40,28 @@ _public_ int cryptsetup_token_open_pin(
                 int token /* is always >= 0 */,
                 const char *pin,
                 size_t pin_size,
-                char **password, /* freed by cryptsetup_token_buffer_free */
-                size_t *password_len,
+                char **ret_password, /* freed by cryptsetup_token_buffer_free */
+                size_t *ret_password_len,
                 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
 
-        int r;
-        const char *json;
-        size_t blob_size, policy_hash_size, decrypted_key_size;
-        uint32_t pcr_mask;
-        uint16_t pcr_bank, primary_alg;
+        _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
+        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL;
+        size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size;
+        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        uint32_t hash_pcr_mask, pubkey_pcr_mask;
         systemd_tpm2_plugin_params params = {
                 .search_pcr_mask = UINT32_MAX
         };
-        _cleanup_free_ void *blob = NULL, *policy_hash = NULL;
-        _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
-        _cleanup_(erase_and_freep) void *decrypted_key = NULL;
-        _cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
+        uint16_t pcr_bank, primary_alg;
+        TPM2Flags flags = 0;
+        const char *json;
+        int r;
 
-        assert(!pin || pin_size);
-        assert(password);
-        assert(password_len);
         assert(token >= 0);
+        assert(!pin || pin_size > 0);
+        assert(ret_password);
+        assert(ret_password_len);
 
         /* This must not fail at this moment (internal error) */
         r = crypt_token_json_get(cd, token, &json);
@@ -74,32 +75,44 @@ _public_ int cryptsetup_token_open_pin(
         if (usrptr)
                 params = *(systemd_tpm2_plugin_params *)usrptr;
 
-        TPM2Flags flags = 0;
-        r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash, &flags);
+        r = json_parse(json, 0, &v, NULL, NULL);
         if (r < 0)
-                return log_debug_open_error(cd, r);
-
-        /* should not happen since cryptsetup_token_validate have passed */
-        r = unbase64mem(base64_blob, SIZE_MAX, &blob, &blob_size);
+                return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m");
+
+        r = tpm2_parse_luks2_json(
+                        v,
+                        NULL,
+                        &hash_pcr_mask,
+                        &pcr_bank,
+                        &pubkey,
+                        &pubkey_size,
+                        &pubkey_pcr_mask,
+                        &primary_alg,
+                        &blob,
+                        &blob_size,
+                        &policy_hash,
+                        &policy_hash_size,
+                        &flags);
         if (r < 0)
                 return log_debug_open_error(cd, r);
 
-        /* should not happen since cryptsetup_token_validate have passed */
-        r = unhexmem(hex_policy_hash, SIZE_MAX, &policy_hash, &policy_hash_size);
-        if (r < 0)
-                return log_debug_open_error(cd, r);
+        if (params.search_pcr_mask != UINT32_MAX && hash_pcr_mask != params.search_pcr_mask)
+                return crypt_log_debug_errno(cd, ENXIO, "PCR mask doesn't match expectation (%" PRIu32 " vs. %" PRIu32 ")", hash_pcr_mask, params.search_pcr_mask);
 
         r = acquire_luks2_key(
-                        pcr_mask,
+                        params.device,
+                        hash_pcr_mask,
                         pcr_bank,
+                        pubkey, pubkey_size,
+                        pubkey_pcr_mask,
+                        params.signature_path,
+                        pin_string,
                         primary_alg,
-                        params.device,
                         blob,
                         blob_size,
                         policy_hash,
                         policy_hash_size,
                         flags,
-                        pin_string,
                         &decrypted_key,
                         &decrypted_key_size);
         if (r < 0)
@@ -111,8 +124,8 @@ _public_ int cryptsetup_token_open_pin(
                 return log_debug_open_error(cd, r);
 
         /* free'd automatically by libcryptsetup */
-        *password_len = strlen(base64_encoded);
-        *password = TAKE_PTR(base64_encoded);
+        *ret_password_len = strlen(base64_encoded);
+        *ret_password = TAKE_PTR(base64_encoded);
 
         return 0;
 }
@@ -133,11 +146,11 @@ _public_ int cryptsetup_token_open_pin(
 _public_ int cryptsetup_token_open(
                 struct crypt_device *cd, /* is always LUKS2 context */
                 int token /* is always >= 0 */,
-                char **password, /* freed by cryptsetup_token_buffer_free */
-                size_t *password_len,
+                char **ret_password, /* freed by cryptsetup_token_buffer_free */
+                size_t *ret_password_len,
                 void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
 
-        return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr);
+        return cryptsetup_token_open_pin(cd, token, NULL, 0, ret_password, ret_password_len, usrptr);
 }
 
 /*
@@ -156,45 +169,66 @@ _public_ void cryptsetup_token_dump(
                 struct crypt_device *cd /* is always LUKS2 context */,
                 const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
 
-        int r;
-        TPM2Flags flags = 0;
-        uint32_t pcr_mask;
+        _cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL;
+        _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL;
+        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
+        size_t blob_size, policy_hash_size, pubkey_size;
+        uint32_t hash_pcr_mask, pubkey_pcr_mask;
         uint16_t pcr_bank, primary_alg;
-        size_t decoded_blob_size;
-        _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL,
-                            *pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL;
-        _cleanup_free_ void *decoded_blob = NULL;
+        TPM2Flags flags = 0;
+        int r;
 
         assert(json);
 
-        r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash, &flags);
+        r = json_parse(json, 0, &v, NULL, NULL);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %m");
+
+        r = tpm2_parse_luks2_json(
+                        v,
+                        NULL,
+                        &hash_pcr_mask,
+                        &pcr_bank,
+                        &pubkey,
+                        &pubkey_size,
+                        &pubkey_pcr_mask,
+                        &primary_alg,
+                        &blob,
+                        &blob_size,
+                        &policy_hash,
+                        &policy_hash_size,
+                        &flags);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
+
+        r = pcr_mask_to_string(hash_pcr_mask, &hash_pcrs_str);
         if (r < 0)
-                return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m.");
+                return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m");
 
-        for (uint32_t i = 0; i < TPM2_PCRS_MAX; i++) {
-                if ((pcr_mask & (UINT32_C(1) << i)) &&
-                    ((r = strextendf_with_separator(&pcrs_str, ", ", "%" PRIu32, i)) < 0))
-                        return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
-        }
+        r = pcr_mask_to_string(pubkey_pcr_mask, &pubkey_pcrs_str);
+        if (r < 0)
+                return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m");
 
-        r = unbase64mem(base64_blob, SIZE_MAX, &decoded_blob, &decoded_blob_size);
+        r = crypt_dump_buffer_to_hex_string(blob, blob_size, &blob_str);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
 
-        r = crypt_dump_buffer_to_hex_string(decoded_blob, decoded_blob_size, &blob_str);
+        r = crypt_dump_buffer_to_hex_string(pubkey, pubkey_size, &pubkey_str);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
 
-        r = crypt_dump_hex_string(hex_policy_hash, &policy_hash_str);
+        r = crypt_dump_buffer_to_hex_string(policy_hash, policy_hash_size, &policy_hash_str);
         if (r < 0)
                 return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
 
-        crypt_log(cd, "\ttpm2-pcrs:  %s\n", strna(pcrs_str));
-        crypt_log(cd, "\ttpm2-bank:  %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank)));
-        crypt_log(cd, "\ttpm2-primary-alg:  %s\n", strna(tpm2_primary_alg_to_string(primary_alg)));
-        crypt_log(cd, "\ttpm2-blob:  %s\n", blob_str);
+        crypt_log(cd, "\ttpm2-hash-pcrs:   %s\n", strna(hash_pcrs_str));
+        crypt_log(cd, "\ttpm2-pcr-bank:    %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank)));
+        crypt_log(cd, "\ttpm2-pubkey:" CRYPT_DUMP_LINE_SEP "%s\n", pubkey_str);
+        crypt_log(cd, "\ttpm2-pubkey-pcrs: %s\n", strna(pubkey_pcrs_str));
+        crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_primary_alg_to_string(primary_alg)));
+        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-pin:         %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
 }
 
 /*
index 3d633de3f55c0dd6198f91f97182627571196948..be496d4949183b08472dc93b51014ee9b41b6162 100644 (file)
 #include "tpm2-util.h"
 
 int acquire_luks2_key(
-                uint32_t pcr_mask,
+                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,
+                const char *pin,
                 uint16_t primary_alg,
-                const char *device,
                 const void *key_data,
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
                 TPM2Flags flags,
-                const char *pin,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size) {
 
+        _cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
         _cleanup_free_ char *auto_device = NULL;
         int r;
 
@@ -45,121 +50,22 @@ int acquire_luks2_key(
         if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
                 return -ENOANO;
 
+        if (pubkey_pcr_mask != 0) {
+                r = tpm2_load_pcr_signature(signature_path, &signature_json);
+                if (r < 0)
+                        return r;
+        }
+
         return tpm2_unseal(
                         device,
-                        pcr_mask,
+                        hash_pcr_mask,
                         pcr_bank,
-                        /* pubkey= */ NULL, /* pubkey_size= */ 0,
-                        /* pubkey_pcr_mask= */ 0,
-                        /* signature_json= */ NULL,
+                        pubkey, pubkey_size,
+                        pubkey_pcr_mask,
+                        signature_json,
                         pin,
                         primary_alg,
                         key_data, key_data_size,
                         policy_hash, policy_hash_size,
                         ret_decrypted_key, ret_decrypted_key_size);
 }
-
-/* this function expects valid "systemd-tpm2" in json */
-int parse_luks2_tpm2_data(
-                const char *json,
-                uint32_t search_pcr_mask,
-                uint32_t *ret_pcr_mask,
-                uint16_t *ret_pcr_bank,
-                uint16_t *ret_primary_alg,
-                char **ret_base64_blob,
-                char **ret_hex_policy_hash,
-                TPM2Flags *ret_flags) {
-
-        int r;
-        JsonVariant *w;
-        uint32_t pcr_mask;
-        uint16_t pcr_bank = UINT16_MAX, primary_alg = TPM2_ALG_ECC;
-        _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL;
-        _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
-        TPM2Flags flags = 0;
-
-        assert(json);
-        assert(ret_pcr_mask);
-        assert(ret_pcr_bank);
-        assert(ret_primary_alg);
-        assert(ret_base64_blob);
-        assert(ret_hex_policy_hash);
-
-        r = json_parse(json, 0, &v, NULL, NULL);
-        if (r < 0)
-                return -EINVAL;
-
-        w = json_variant_by_key(v, "tpm2-pcrs");
-        if (!w)
-                return -EINVAL;
-
-        r = tpm2_parse_pcr_json_array(w, &pcr_mask);
-        if (r < 0)
-                return r;
-
-        if (search_pcr_mask != UINT32_MAX &&
-            search_pcr_mask != pcr_mask)
-                return -ENXIO;
-
-        w = json_variant_by_key(v, "tpm2-pcr-bank");
-        if (w) {
-                /* The PCR bank field is optional */
-
-                if (!json_variant_is_string(w))
-                        return -EINVAL;
-
-                r = tpm2_pcr_bank_from_string(json_variant_string(w));
-                if (r < 0)
-                        return r;
-
-                pcr_bank = r;
-        }
-
-        w = json_variant_by_key(v, "tpm2-primary-alg");
-        if (w) {
-                /* The primary key algorithm is optional */
-
-                if (!json_variant_is_string(w))
-                        return -EINVAL;
-
-                r = tpm2_primary_alg_from_string(json_variant_string(w));
-                if (r < 0)
-                        return r;
-
-                primary_alg = r;
-        }
-
-        w = json_variant_by_key(v, "tpm2-blob");
-        if (!w || !json_variant_is_string(w))
-                return -EINVAL;
-
-        base64_blob = strdup(json_variant_string(w));
-        if (!base64_blob)
-                return -ENOMEM;
-
-        w = json_variant_by_key(v, "tpm2-policy-hash");
-        if (!w || !json_variant_is_string(w))
-                return -EINVAL;
-
-        hex_policy_hash = strdup(json_variant_string(w));
-        if (!hex_policy_hash)
-                return -ENOMEM;
-
-        w = json_variant_by_key(v, "tpm2-pin");
-        if (w) {
-                if (!json_variant_is_boolean(w))
-                        return -EINVAL;
-
-                if (json_variant_boolean(w))
-                        flags |= TPM2_FLAGS_USE_PIN;
-        }
-
-        *ret_pcr_mask = pcr_mask;
-        *ret_pcr_bank = pcr_bank;
-        *ret_primary_alg = primary_alg;
-        *ret_base64_blob = TAKE_PTR(base64_blob);
-        *ret_hex_policy_hash = TAKE_PTR(hex_policy_hash);
-        *ret_flags = flags;
-
-        return 0;
-}
index 5e3341802520354f227fb27484d1cba15655de36..f3625124e53f07064fdec4eb3f630a8aa80a81f0 100644 (file)
@@ -7,25 +7,19 @@
 struct crypt_device;
 
 int acquire_luks2_key(
+                const char *device,
                 uint32_t pcr_mask,
                 uint16_t pcr_bank,
+                const void *pubkey,
+                size_t pubkey_size,
+                uint32_t pubkey_pcr_mask,
+                const char *signature_path,
+                const char *pin,
                 uint16_t primary_alg,
-                const char *device,
                 const void *key_data,
                 size_t key_data_size,
                 const void *policy_hash,
                 size_t policy_hash_size,
                 TPM2Flags flags,
-                const char *pin,
                 void **ret_decrypted_key,
                 size_t *ret_decrypted_key_size);
-
-int parse_luks2_tpm2_data(
-                const char *json,
-                uint32_t search_pcr_mask,
-                uint32_t *ret_pcr_mask,
-                uint16_t *ret_pcr_bank,
-                uint16_t *ret_primary_alg,
-                char **ret_base64_blob,
-                char **ret_hex_policy_hash,
-                TPM2Flags *ret_flags);
index 406369329c365c8c274cacccb9f53ba9c05278ca..d6e5833b2402af14bebc14c94f5883610d1d6e8a 100644 (file)
@@ -1396,7 +1396,8 @@ static int attach_luks2_by_tpm2_via_plugin(
 #if HAVE_LIBCRYPTSETUP_PLUGINS
         systemd_tpm2_plugin_params params = {
                 .search_pcr_mask = arg_tpm2_pcr_mask,
-                .device = arg_tpm2_device
+                .device = arg_tpm2_device,
+                .signature_path = arg_tpm2_signature,
         };
 
         if (!libcryptsetup_plugins_support())
index 554ca58df6a76eaeb6afea0fbb3db1d9afad57b0..cd6bcc91b31120d7413f721da2ccf7f72b4890c4 100644 (file)
@@ -128,6 +128,7 @@ int tpm2_primary_alg_from_string(const char *alg);
 typedef struct {
         uint32_t search_pcr_mask;
         const char *device;
+        const char *signature_path;
 } systemd_tpm2_plugin_params;
 
 typedef enum Tpm2Support {