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:
parent
4d5cc0d453
commit
75a9681ec0
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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())
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user