1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-30 06:25:37 +03:00

cryptsetup: hook up TPM2 token code with policies based on PCR signatures, too

This commit is contained in:
Lennart Poettering 2022-08-19 22:18:40 +02:00
parent 4d5cc0d453
commit 75a9681ec0
5 changed files with 118 additions and 182 deletions

View 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 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 = unbase64mem(base64_blob, SIZE_MAX, &blob, &blob_size);
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,
pcr_bank,
primary_alg,
params.device,
hash_pcr_mask,
pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
params.signature_path,
pin_string,
primary_alg,
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,41 +169,62 @@ _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 " metadata: %m.");
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %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 = 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 = unbase64mem(base64_blob, SIZE_MAX, &decoded_blob, &decoded_blob_size);
r = pcr_mask_to_string(hash_pcr_mask, &hash_pcrs_str);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %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 = 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-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);

View File

@ -13,19 +13,24 @@
#include "tpm2-util.h"
int acquire_luks2_key(
uint32_t pcr_mask,
uint16_t pcr_bank,
uint16_t primary_alg,
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 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;
}

View 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);

View 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())

View 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 {