Switch crypt_activate_by_signed_key to keyslot context based activation.

It introduces new keyslot context type CRYPT_KC_TYPE_SIGNED_KEY.
This commit is contained in:
Ondrej Kozina
2023-07-20 17:16:29 +02:00
committed by Daniel Zatovic
parent e5bd99665e
commit e43de57fac
7 changed files with 263 additions and 114 deletions

View File

@@ -211,6 +211,48 @@ static int get_generic_volume_key_by_key(struct crypt_device *cd,
return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
}
static int get_generic_signed_key_by_key(struct crypt_device *cd,
struct crypt_keyslot_context *kc,
struct volume_key **r_vk,
struct volume_key **r_signature)
{
struct volume_key *vk, *vk_sig;
assert(kc && ((kc->type == CRYPT_KC_TYPE_KEY) ||
(kc->type == CRYPT_KC_TYPE_SIGNED_KEY)));
assert(r_vk);
assert(r_signature);
/* return key with no signature */
if (kc->type == CRYPT_KC_TYPE_KEY) {
*r_signature = NULL;
return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk);
}
if (!kc->u.ks.volume_key || !kc->u.ks.signature) {
kc->error = -EINVAL;
return kc->error;
}
vk = crypt_alloc_volume_key(kc->u.ks.volume_key_size, kc->u.ks.volume_key);
if (!vk) {
kc->error = -ENOMEM;
return kc->error;
}
vk_sig = crypt_alloc_volume_key(kc->u.ks.signature_size, kc->u.ks.signature);
if (!vk_sig) {
crypt_free_volume_key(vk);
kc->error = -ENOMEM;
return kc->error;
}
*r_vk = vk;
*r_signature = vk_sig;
return 0;
}
static int get_luks2_key_by_token(struct crypt_device *cd,
struct crypt_keyslot_context *kc,
int keyslot,
@@ -445,11 +487,36 @@ void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc,
kc->get_plain_volume_key = get_generic_volume_key_by_key;
kc->get_bitlk_volume_key = get_generic_volume_key_by_key;
kc->get_fvault2_volume_key = get_generic_volume_key_by_key;
kc->get_verity_volume_key = get_generic_volume_key_by_key;
kc->get_verity_volume_key = get_generic_signed_key_by_key;
kc->get_integrity_volume_key = get_generic_volume_key_by_key;
unlock_method_init_internal(kc);
}
void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_context *kc,
const char *volume_key,
size_t volume_key_size,
const char *signature,
size_t signature_size)
{
assert(kc);
kc->type = CRYPT_KC_TYPE_SIGNED_KEY;
kc->u.ks.volume_key = volume_key;
kc->u.ks.volume_key_size = volume_key_size;
kc->u.ks.signature = signature;
kc->u.ks.signature_size = signature_size;
kc->get_luks2_key = NULL;
kc->get_luks2_volume_key = NULL;
kc->get_luks1_volume_key = NULL;
kc->get_passphrase = NULL;
kc->get_plain_volume_key = NULL;
kc->get_bitlk_volume_key = NULL;
kc->get_fvault2_volume_key = NULL;
kc->get_verity_volume_key = get_generic_signed_key_by_key;
kc->get_integrity_volume_key = NULL;
unlock_method_init_internal(kc);
}
void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_context *kc,
const char *passphrase,
size_t passphrase_size)
@@ -648,6 +715,30 @@ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd,
return 0;
}
int crypt_keyslot_context_init_by_signed_key(struct crypt_device *cd,
const char *volume_key,
size_t volume_key_size,
const char *signature,
size_t signature_size,
struct crypt_keyslot_context **kc)
{
struct crypt_keyslot_context *tmp;
if (!kc)
return -EINVAL;
tmp = malloc(sizeof(*tmp));
if (!tmp)
return -ENOMEM;
crypt_keyslot_unlock_by_signed_key_init_internal(tmp, volume_key, volume_key_size,
signature, signature_size);
*kc = tmp;
return 0;
}
int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd,
const char *key_description,
struct crypt_keyslot_context **kc)
@@ -729,6 +820,8 @@ const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc)
return "keyring";
case CRYPT_KC_TYPE_VK_KEYRING:
return "volume key in keyring";
case CRYPT_KC_TYPE_SIGNED_KEY:
return "signed key";
default:
return "<unknown>";
}

View File

@@ -45,6 +45,12 @@ typedef int (*keyslot_context_get_generic_volume_key) (
struct crypt_keyslot_context *kc,
struct volume_key **r_vk);
typedef int (*keyslot_context_get_generic_signed_key) (
struct crypt_device *cd,
struct crypt_keyslot_context *kc,
struct volume_key **r_vk,
struct volume_key **r_signature);
typedef int (*keyslot_context_get_passphrase) (
struct crypt_device *cd,
struct crypt_keyslot_context *kc,
@@ -76,6 +82,12 @@ struct crypt_keyslot_context {
const char *volume_key;
size_t volume_key_size;
} k;
struct {
const char *volume_key;
size_t volume_key_size;
const char *signature;
size_t signature_size;
} ks;
struct {
const char *key_description;
} kr;
@@ -97,7 +109,7 @@ struct crypt_keyslot_context {
keyslot_context_get_generic_volume_key get_plain_volume_key;
keyslot_context_get_generic_volume_key get_bitlk_volume_key;
keyslot_context_get_generic_volume_key get_fvault2_volume_key;
keyslot_context_get_generic_volume_key get_verity_volume_key;
keyslot_context_get_generic_signed_key get_verity_volume_key;
keyslot_context_get_generic_volume_key get_integrity_volume_key;
keyslot_context_get_passphrase get_passphrase;
};
@@ -108,6 +120,12 @@ void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc,
const char *volume_key,
size_t volume_key_size);
void crypt_keyslot_unlock_by_signed_key_init_internal(struct crypt_keyslot_context *kc,
const char *volume_key,
size_t volume_key_size,
const char *signature,
size_t signature_size);
void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_context *kc,
const char *passphrase,
size_t passphrase_size);

View File

@@ -1279,6 +1279,28 @@ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd,
size_t volume_key_size,
struct crypt_keyslot_context **kc);
/**
* Initialize keyslot context via signed key.
*
* @param cd crypt device handle initialized to device context
*
* @param volume_key provided volume key
* @param volume_key_size size of volume_key
* @param signature buffer with signature for the key
* @param signature_size bsize of signature buffer
* @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_SIGNED_KEY
*
* @return zero on success or negative errno otherwise.
*
* @note currently supported only with VERITY devices.
*/
int crypt_keyslot_context_init_by_signed_key(struct crypt_device *cd,
const char *volume_key,
size_t volume_key_size,
const char *signature,
size_t signature_size,
struct crypt_keyslot_context **kc);
/**
* Initialize keyslot context via passphrase stored in a keyring.
*
@@ -1361,6 +1383,8 @@ int crypt_keyslot_context_set_pin(struct crypt_device *cd,
* (@link crypt_keyslot_context_init_by_vk_in_keyring @endlink)
*/
#define CRYPT_KC_TYPE_VK_KEYRING INT16_C(6)
/** keyslot context initialized by signed key (@link crypt_keyslot_context_init_by_signed_key @endlink) */
#define CRYPT_KC_TYPE_SIGNED_KEY INT16_C(7)
/** @} */
/**

View File

@@ -175,6 +175,7 @@ CRYPTSETUP_2.7 {
crypt_get_vk_keyring_type;
crypt_keyslot_context_init_by_keyring;
crypt_keyslot_context_init_by_vk_in_keyring;
crypt_keyslot_context_init_by_signed_key;
crypt_resume_by_keyslot_context;
crypt_set_keyring_to_link;
crypt_set_vk_keyring_type;

View File

@@ -5130,9 +5130,9 @@ static int _activate_by_volume_key(struct crypt_device *cd,
assert(crypt_volume_key_get_id(vk) == LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT));
r = LUKS2_activate(cd, name, vk, external_key, flags);
} else if (isVERITY(cd->type)) {
assert(!external_key);
assert(crypt_volume_key_get_id(vk) == KEY_VERIFIED);
r = crypt_activate_by_signed_key(cd, name, vk->key, vk->keylength, NULL, 0, flags);
r = VERITY_activate(cd, name, vk, external_key, cd->u.verity.fec_device,
&cd->u.verity.hdr, flags);
} else if (isTCRYPT(cd->type)) {
assert(!external_key);
r = TCRYPT_activate(cd, name, &cd->u.tcrypt.hdr,
@@ -5163,7 +5163,8 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
uint32_t flags)
{
bool use_keyring;
struct volume_key *p_ext_key, *crypt_key = NULL, *opal_key = NULL, *vk = NULL, *p_crypt = NULL;
struct volume_key *p_ext_key, *crypt_key = NULL, *opal_key = NULL, *vk = NULL,
*vk_sign = NULL, *p_crypt = NULL;
size_t passphrase_size;
const char *passphrase = NULL;
int unlocked_keyslot, r = -EINVAL;
@@ -5179,10 +5180,14 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
return -EINVAL;
if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name)
return -EINVAL;
if (kc->type == CRYPT_KC_TYPE_KEYRING && !kernel_keyring_support()) {
if ((kc->type == CRYPT_KC_TYPE_KEYRING) && !kernel_keyring_support()) {
log_err(cd, _("Kernel keyring is not supported by the kernel."));
return -EINVAL;
}
if ((kc->type == CRYPT_KC_TYPE_SIGNED_KEY) && !kernel_keyring_support()) {
log_err(cd, _("Kernel keyring missing: required for passing signature to kernel."));
return -EINVAL;
}
r = _check_header_data_overlap(cd, name);
if (r < 0)
return r;
@@ -5195,6 +5200,7 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size);
if (r < 0)
return r;
/* TODO: Only loopaes should by activated by passphrase method */
if (passphrase) {
if (isLOOPAES(cd->type))
return _activate_loopaes(cd, name, passphrase, passphrase_size, flags);
@@ -5227,9 +5233,16 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
} else if (isFVAULT2(cd->type)) {
if (kc->get_fvault2_volume_key)
r = kc->get_fvault2_volume_key(cd, kc, &vk);
} else if (isVERITY(cd->type)) {
} else if (isVERITY(cd->type) && (name || kc->type != CRYPT_KC_TYPE_SIGNED_KEY)) {
if (kc->get_verity_volume_key)
r = kc->get_verity_volume_key(cd, kc, &vk);
r = kc->get_verity_volume_key(cd, kc, &vk, &vk_sign);
if (r >= 0)
r = VERITY_verify_params(cd, &cd->u.verity.hdr, vk_sign != NULL,
cd->u.verity.fec_device, vk);
free(CONST_CAST(void*)cd->u.verity.root_hash);
cd->u.verity.root_hash = NULL;
flags |= CRYPT_ACTIVATE_READONLY;
} else if (isINTEGRITY(cd->type)) {
if (kc->get_integrity_volume_key)
r = kc->get_integrity_volume_key(cd, kc, &vk);
@@ -5255,12 +5268,6 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
if (r < 0)
goto out;
if (!name && isVERITY(cd->type)) {
r = crypt_activate_by_signed_key(cd, name, vk->key, vk->keylength, NULL, 0, flags);
if (r < 0)
goto out;
}
if (isLUKS2(cd->type)) {
if (LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
@@ -5296,7 +5303,7 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd,
}
} else {
p_crypt = vk;
p_ext_key = NULL;
p_ext_key = vk_sign;
}
if (name)
@@ -5311,6 +5318,7 @@ out:
crypt_free_volume_key(vk);
crypt_free_volume_key(crypt_key);
crypt_free_volume_key(opal_key);
crypt_free_volume_key(vk_sign);
return r;
}
@@ -5396,8 +5404,8 @@ int crypt_activate_by_signed_key(struct crypt_device *cd,
size_t signature_size,
uint32_t flags)
{
char description[512];
int r;
struct crypt_keyslot_context kc;
if (!cd || !isVERITY(cd->type))
return -EINVAL;
@@ -5407,57 +5415,13 @@ int crypt_activate_by_signed_key(struct crypt_device *cd,
return -EINVAL;
}
if (name)
log_dbg(cd, "Activating volume %s by %skey.", name, signature ? "signed " : "");
else
log_dbg(cd, "Checking volume by key.");
if (cd->u.verity.hdr.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE && !signature) {
log_err(cd, _("Root hash signature required."));
return -EINVAL;
}
r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH);
if (r < 0)
return r;
if (signature && !kernel_keyring_support()) {
log_err(cd, _("Kernel keyring missing: required for passing signature to kernel."));
return -EINVAL;
}
/* volume_key == root hash */
free(CONST_CAST(void*)cd->u.verity.root_hash);
cd->u.verity.root_hash = NULL;
if (signature) {
r = snprintf(description, sizeof(description)-1, "cryptsetup:%s%s%s",
crypt_get_uuid(cd) ?: "", crypt_get_uuid(cd) ? "-" : "", name);
if (r < 0)
return -EINVAL;
log_dbg(cd, "Adding signature into keyring %s", description);
r = keyring_add_key_in_thread_keyring(USER_KEY, description, signature, signature_size);
if (r) {
log_err(cd, _("Failed to load key in kernel keyring."));
return r;
}
}
r = VERITY_activate(cd, name, volume_key, volume_key_size,
signature ? description : NULL,
cd->u.verity.fec_device,
&cd->u.verity.hdr, flags | CRYPT_ACTIVATE_READONLY);
if (!r) {
cd->u.verity.root_hash_size = volume_key_size;
cd->u.verity.root_hash = malloc(volume_key_size);
if (cd->u.verity.root_hash)
memcpy(CONST_CAST(void*)cd->u.verity.root_hash, volume_key, volume_key_size);
}
if (signature)
crypt_drop_keyring_key_by_description(cd, description, USER_KEY);
crypt_keyslot_unlock_by_signed_key_init_internal(&kc, volume_key, volume_key_size,
signature, signature_size);
else
crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size);
r = crypt_activate_by_keyslot_context(cd, name, -2 /* unused */, &kc, flags);
crypt_keyslot_context_destroy_internal(&kc);
return r;
}

View File

@@ -251,91 +251,131 @@ int VERITY_UUID_generate(char **uuid_string)
return 0;
}
int VERITY_verify_params(struct crypt_device *cd,
struct crypt_params_verity *hdr,
bool signed_root_hash,
struct device *fec_device,
struct volume_key *root_hash)
{
bool userspace_verification;
int v, r;
unsigned int fec_errors = 0;
assert(cd);
assert(hdr);
assert(root_hash);
log_dbg(cd, "Verifying VERITY device using hash %s.",
hdr->hash_name);
userspace_verification = hdr->flags & CRYPT_VERITY_CHECK_HASH;
if (userspace_verification && signed_root_hash) {
log_err(cd, _("Root hash signature verification is not supported."));
return -EINVAL;
}
if ((hdr->flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE) && !signed_root_hash) {
log_err(cd, _("Root hash signature required."));
return -EINVAL;
}
if (!userspace_verification)
return 0;
log_dbg(cd, "Verification of VERITY data in userspace required.");
r = VERITY_verify(cd, hdr, root_hash->key, root_hash->keylength);
if ((r == -EPERM || r == -EFAULT) && fec_device) {
v = r;
log_dbg(cd, "Verification failed, trying to repair with FEC device.");
r = VERITY_FEC_process(cd, hdr, fec_device, 1, &fec_errors);
if (r < 0)
log_err(cd, _("Errors cannot be repaired with FEC device."));
else if (fec_errors) {
log_err(cd, _("Found %u repairable errors with FEC device."),
fec_errors);
/* If root hash failed, we cannot be sure it was properly repaired */
}
if (v == -EFAULT)
r = -EPERM;
}
return r;
}
/* Activate verity device in kernel device-mapper */
int VERITY_activate(struct crypt_device *cd,
const char *name,
const char *root_hash,
size_t root_hash_size,
const char *signature_description,
struct volume_key *root_hash,
struct volume_key *signature,
struct device *fec_device,
struct crypt_params_verity *verity_hdr,
uint32_t activation_flags)
{
uint32_t dmv_flags;
unsigned int fec_errors = 0;
int r, v;
struct crypt_dm_active_device dmd = {
.size = verity_hdr->data_size * verity_hdr->data_block_size / 512,
.flags = activation_flags,
.uuid = crypt_get_uuid(cd),
};
int r;
char *description = NULL;
struct crypt_dm_active_device dmd = { 0 };
log_dbg(cd, "Trying to activate VERITY device %s using hash %s.",
name ?: "[none]", verity_hdr->hash_name);
assert(name);
assert(root_hash);
assert(verity_hdr);
if (verity_hdr->flags & CRYPT_VERITY_CHECK_HASH) {
if (signature_description) {
log_err(cd, _("Root hash signature verification is not supported."));
return -EINVAL;
}
dmd.size = verity_hdr->data_size * verity_hdr->data_block_size / 512;
dmd.flags = activation_flags;
dmd.uuid = crypt_get_uuid(cd);
log_dbg(cd, "Verification of data in userspace required.");
r = VERITY_verify(cd, verity_hdr, root_hash, root_hash_size);
if ((r == -EPERM || r == -EFAULT) && fec_device) {
v = r;
log_dbg(cd, "Verification failed, trying to repair with FEC device.");
r = VERITY_FEC_process(cd, verity_hdr, fec_device, 1, &fec_errors);
if (r < 0)
log_err(cd, _("Errors cannot be repaired with FEC device."));
else if (fec_errors) {
log_err(cd, _("Found %u repairable errors with FEC device."),
fec_errors);
/* If root hash failed, we cannot be sure it was properly repaired */
}
if (v == -EFAULT)
r = -EPERM;
}
log_dbg(cd, "Activating VERITY device %s using hash %s.",
name, verity_hdr->hash_name);
if (signature) {
r = asprintf(&description, "cryptsetup:%s%s%s",
crypt_get_uuid(cd) ?: "", crypt_get_uuid(cd) ? "-" : "", name);
if (r < 0)
return r;
}
return -EINVAL;
if (!name)
return 0;
log_dbg(cd, "Adding signature into keyring %s", description);
r = keyring_add_key_in_thread_keyring(USER_KEY, description, signature->key, signature->keylength);
if (r) {
log_err(cd, _("Failed to load key in kernel keyring."));
free(description);
return r;
}
}
r = device_block_adjust(cd, crypt_metadata_device(cd), DEV_OK,
0, NULL, NULL);
if (r)
return r;
goto out;
r = device_block_adjust(cd, crypt_data_device(cd), DEV_EXCL,
0, &dmd.size, &dmd.flags);
if (r)
return r;
goto out;
if (fec_device) {
r = device_block_adjust(cd, fec_device, DEV_OK,
0, NULL, NULL);
if (r)
return r;
goto out;
}
r = dm_verity_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
crypt_metadata_device(cd), fec_device, root_hash,
root_hash_size, signature_description,
crypt_metadata_device(cd), fec_device, root_hash->key,
root_hash->keylength, description,
VERITY_hash_offset_block(verity_hdr),
VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr);
if (r)
return r;
goto out;
r = dm_create_device(cd, name, CRYPT_VERITY, &dmd);
if (r < 0 && (dm_flags(cd, DM_VERITY, &dmv_flags) || !(dmv_flags & DM_VERITY_SUPPORTED))) {
log_err(cd, _("Kernel does not support dm-verity mapping."));
r = -ENOTSUP;
}
if (r < 0 && signature_description && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
if (r < 0 && signature && !(dmv_flags & DM_VERITY_SIGNATURE_SUPPORTED)) {
log_err(cd, _("Kernel does not support dm-verity signature option."));
r = -ENOTSUP;
}
@@ -351,6 +391,8 @@ int VERITY_activate(struct crypt_device *cd,
r = 0;
out:
crypt_drop_keyring_key_by_description(cd, description, USER_KEY);
free(description);
dm_targets_free(cd, &dmd);
return r;
}

View File

@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define VERITY_MAX_HASH_TYPE 1
#define VERITY_BLOCK_SIZE_OK(x) ((x) % 512 || (x) < 512 || \
@@ -31,6 +32,7 @@
struct crypt_device;
struct crypt_params_verity;
struct device;
struct volume_key;
int VERITY_read_sb(struct crypt_device *cd,
uint64_t sb_offset,
@@ -44,13 +46,18 @@ int VERITY_write_sb(struct crypt_device *cd,
int VERITY_activate(struct crypt_device *cd,
const char *name,
const char *root_hash,
size_t root_hash_size,
const char *signature_description,
struct volume_key *root_hash,
struct volume_key *signature,
struct device *fec_device,
struct crypt_params_verity *verity_hdr,
uint32_t activation_flags);
int VERITY_verify_params(struct crypt_device *cd,
struct crypt_params_verity *hdr,
bool signed_root_hash,
struct device *fec_device,
struct volume_key *root_hash);
int VERITY_verify(struct crypt_device *cd,
struct crypt_params_verity *verity_hdr,
const char *root_hash,