From 58385d68d8f4115ab103f22cfaba654f2d0f2136 Mon Sep 17 00:00:00 2001 From: Daniel Zatovic Date: Thu, 20 Apr 2023 10:03:49 +0200 Subject: [PATCH] Allow activation via keyslot context. --- lib/keyslot_context.c | 34 ++++- lib/keyslot_context.h | 18 ++- lib/libcryptsetup.h | 19 +++ lib/libcryptsetup.sym | 1 + lib/luks2/luks2_digest.c | 12 ++ lib/setup.c | 282 ++++++++++++++++++++++++--------------- 6 files changed, 255 insertions(+), 111 deletions(-) diff --git a/lib/keyslot_context.c b/lib/keyslot_context.c index 631f8dce..ab1ab320 100644 --- a/lib/keyslot_context.c +++ b/lib/keyslot_context.c @@ -204,6 +204,13 @@ static int get_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_volume_key_by_key(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + struct volume_key **r_vk) +{ + return get_key_by_key(cd, kc, -2 /* unused */, -2 /* unused */, r_vk); +} + static int get_luks2_key_by_token(struct crypt_device *cd, struct crypt_keyslot_context *kc, int keyslot, @@ -211,12 +218,17 @@ static int get_luks2_key_by_token(struct crypt_device *cd, struct volume_key **r_vk) { int r; + struct luks2_hdr *hdr; assert(cd); assert(kc && kc->type == CRYPT_KC_TYPE_TOKEN); assert(r_vk); - r = LUKS2_token_unlock_key(cd, crypt_get_hdr(cd, CRYPT_LUKS2), keyslot, kc->u.t.id, kc->u.t.type, + hdr = crypt_get_hdr(cd, CRYPT_LUKS2); + if (!hdr) + return -EINVAL; + + r = LUKS2_token_unlock_key(cd, hdr, keyslot, kc->u.t.id, kc->u.t.type, kc->u.t.pin, kc->u.t.pin_size, segment, kc->u.t.usrptr, r_vk); if (r < 0) kc->error = r; @@ -283,6 +295,11 @@ void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc, kc->get_luks2_volume_key = get_volume_key_by_key; kc->get_luks1_volume_key = get_volume_key_by_key; kc->get_passphrase = NULL; /* keyslot key context does not provide passphrase */ + 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_integrity_volume_key = get_generic_volume_key_by_key; unlock_method_init_internal(kc); } @@ -299,6 +316,11 @@ void crypt_keyslot_unlock_by_passphrase_init_internal(struct crypt_keyslot_conte kc->get_luks2_volume_key = get_luks2_volume_key_by_passphrase; kc->get_luks1_volume_key = get_luks1_volume_key_by_passphrase; kc->get_passphrase = get_passphrase_by_passphrase; + kc->get_plain_volume_key = NULL; + kc->get_bitlk_volume_key = NULL; + kc->get_fvault2_volume_key = NULL; + kc->get_verity_volume_key = NULL; + kc->get_integrity_volume_key = NULL; unlock_method_init_internal(kc); } @@ -317,6 +339,11 @@ void crypt_keyslot_unlock_by_keyfile_init_internal(struct crypt_keyslot_context kc->get_luks2_volume_key = get_luks2_volume_key_by_keyfile; kc->get_luks1_volume_key = get_luks1_volume_key_by_keyfile; kc->get_passphrase = get_passphrase_by_keyfile; + kc->get_plain_volume_key = NULL; + kc->get_bitlk_volume_key = NULL; + kc->get_fvault2_volume_key = NULL; + kc->get_verity_volume_key = NULL; + kc->get_integrity_volume_key = NULL; unlock_method_init_internal(kc); } @@ -339,6 +366,11 @@ void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *k kc->get_luks2_volume_key = get_luks2_volume_key_by_token; kc->get_luks1_volume_key = NULL; /* LUKS1 is not supported */ kc->get_passphrase = get_passphrase_by_token; + kc->get_plain_volume_key = NULL; + kc->get_bitlk_volume_key = NULL; + kc->get_fvault2_volume_key = NULL; + kc->get_verity_volume_key = NULL; + kc->get_integrity_volume_key = NULL; unlock_method_init_internal(kc); } diff --git a/lib/keyslot_context.h b/lib/keyslot_context.h index 7ca7428b..6e7e406f 100644 --- a/lib/keyslot_context.h +++ b/lib/keyslot_context.h @@ -40,6 +40,11 @@ typedef int (*keyslot_context_get_volume_key) ( int keyslot, struct volume_key **r_vk); +typedef int (*keyslot_context_get_generic_volume_key) ( + struct crypt_device *cd, + struct crypt_keyslot_context *kc, + struct volume_key **r_vk); + typedef int (*keyslot_context_get_passphrase) ( struct crypt_device *cd, struct crypt_keyslot_context *kc, @@ -78,10 +83,15 @@ struct crypt_keyslot_context { char *i_passphrase; size_t i_passphrase_size; - keyslot_context_get_key get_luks2_key; - keyslot_context_get_volume_key get_luks1_volume_key; - keyslot_context_get_volume_key get_luks2_volume_key; - keyslot_context_get_passphrase get_passphrase; + keyslot_context_get_key get_luks2_key; + keyslot_context_get_volume_key get_luks1_volume_key; + keyslot_context_get_volume_key get_luks2_volume_key; + 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_volume_key get_integrity_volume_key; + keyslot_context_get_passphrase get_passphrase; }; void crypt_keyslot_context_destroy_internal(struct crypt_keyslot_context *method); diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 5bf0d208..d3222353 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -1541,6 +1541,25 @@ int crypt_persistent_flags_get(struct crypt_device *cd, * @{ */ +/** + * Activate device or check using keyslot context. + * + * @param cd crypt device handle + * @param name name of device to create, if @e NULL only check passphrase + * @param keyslot requested keyslot to check or @e CRYPT_ANY_SLOT, keyslot is + * ignored for unlock methods not based on passphrase + * @param kc keyslot context providing volume key or passphrase. + * @param flags activation flags + * + * @return unlocked key slot number for passphrase-based unlock, zero for other + * unlock methods (e.g. volume key context) or negative errno on error. + */ +int crypt_activate_by_keyslot_context(struct crypt_device *cd, + const char *name, + int keyslot, + struct crypt_keyslot_context *kc, + uint32_t flags); + /** * Activate device or check passphrase. * diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index 68118ea2..a4fce23a 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -168,6 +168,7 @@ CRYPTSETUP_2.6 { CRYPTSETUP_2.7 { global: + crypt_activate_by_keyslot_context; crypt_format_luks2_opal; crypt_get_hw_encryption_type; crypt_get_hw_encryption_key_size; diff --git a/lib/luks2/luks2_digest.c b/lib/luks2/luks2_digest.c index 933b0595..a16c50e5 100644 --- a/lib/luks2/luks2_digest.c +++ b/lib/luks2/luks2_digest.c @@ -174,6 +174,18 @@ int LUKS2_digest_verify_by_segment(struct crypt_device *cd, int segment, const struct volume_key *vk) { + int r = -EINVAL; + unsigned s; + + if (segment == CRYPT_ANY_SEGMENT) { + for (s = 0; s < json_segments_count(LUKS2_get_segments_jobj(hdr)); s++) { + if ((r = LUKS2_digest_verify_by_digest(cd, LUKS2_digest_by_segment(hdr, s), vk)) >= 0) + return r; + } + + return -EPERM; + } + return LUKS2_digest_verify_by_digest(cd, LUKS2_digest_by_segment(hdr, segment), vk); } diff --git a/lib/setup.c b/lib/setup.c index 24ae18f6..6ba247ed 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -5070,16 +5070,23 @@ out: static int _activate_loopaes(struct crypt_device *cd, const char *name, - char *buffer, + const char *buffer, size_t buffer_size, uint32_t flags) { int r; unsigned int key_count = 0; struct volume_key *vk = NULL; + char *buffer_copy; + + buffer_copy = crypt_safe_alloc(buffer_size); + if (!buffer_copy) + return -ENOMEM; + memcpy(buffer_copy, buffer, buffer_size); r = LOOPAES_parse_keyfile(cd, &vk, cd->u.loopaes.hdr.hash, &key_count, - buffer, buffer_size); + buffer_copy, buffer_size); + crypt_safe_free(buffer_copy); if (!r && name) r = LOOPAES_activate(cd, name, cd->u.loopaes.cipher, key_count, @@ -5115,91 +5122,7 @@ static int _activate_check_status(struct crypt_device *cd, const char *name, uns } // activation/deactivation of device mapping -int crypt_activate_by_passphrase(struct crypt_device *cd, - const char *name, - int keyslot, - const char *passphrase, - size_t passphrase_size, - uint32_t flags) -{ - int r; - - if (!cd || !passphrase || (!name && (flags & CRYPT_ACTIVATE_REFRESH))) - return -EINVAL; - - log_dbg(cd, "%s volume %s [keyslot %d] using passphrase.", - name ? "Activating" : "Checking", name ?: "passphrase", - keyslot); - - r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH); - if (r < 0) - return r; - - return _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags); -} - -int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd, - const char *name, - int keyslot, - const char *keyfile, - size_t keyfile_size, - uint64_t keyfile_offset, - uint32_t flags) -{ - char *passphrase_read = NULL; - size_t passphrase_size_read; - int r; - - if (!cd || !keyfile || - ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd))) - return -EINVAL; - - log_dbg(cd, "%s volume %s [keyslot %d] using keyfile %s.", - name ? "Activating" : "Checking", name ?: "passphrase", keyslot, keyfile); - - r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH); - if (r < 0) - return r; - - r = crypt_keyfile_device_read(cd, keyfile, - &passphrase_read, &passphrase_size_read, - keyfile_offset, keyfile_size, 0); - if (r < 0) - goto out; - - if (isLOOPAES(cd->type)) - r = _activate_loopaes(cd, name, passphrase_read, passphrase_size_read, flags); - else - r = _activate_by_passphrase(cd, name, keyslot, passphrase_read, passphrase_size_read, flags); - -out: - crypt_safe_free(passphrase_read); - return r; -} - -int crypt_activate_by_keyfile(struct crypt_device *cd, - const char *name, - int keyslot, - const char *keyfile, - size_t keyfile_size, - uint32_t flags) -{ - return crypt_activate_by_keyfile_device_offset(cd, name, keyslot, keyfile, - keyfile_size, 0, flags); -} - -int crypt_activate_by_keyfile_offset(struct crypt_device *cd, - const char *name, - int keyslot, - const char *keyfile, - size_t keyfile_size, - size_t keyfile_offset, - uint32_t flags) -{ - return crypt_activate_by_keyfile_device_offset(cd, name, keyslot, keyfile, - keyfile_size, keyfile_offset, flags); -} -int crypt_activate_by_volume_key(struct crypt_device *cd, +static int _activate_by_volume_key(struct crypt_device *cd, const char *name, const char *volume_key, size_t volume_key_size, @@ -5275,7 +5198,9 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, if (!vk) return -ENOMEM; - r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); + r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, + flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY ? CRYPT_ANY_SEGMENT : CRYPT_DEFAULT_SEGMENT, + vk); if (r == -EPERM || r == -ENOENT) log_err(cd, _("Volume key does not match the volume.")); if (r > 0) @@ -5353,6 +5278,164 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, return r; } +int crypt_activate_by_keyslot_context(struct crypt_device *cd, + const char *name, + int keyslot, + struct crypt_keyslot_context *kc, + uint32_t flags) +{ + struct volume_key *vk = NULL; + size_t passphrase_size; + const char *passphrase = NULL; + int unlocked_keyslot, r = -EINVAL; + + log_dbg(cd, "%s volume %s using %s.", + name ? "Activating" : "Checking", name ?: "passphrase", keyslot_context_type_string(kc)); + + if (!cd || !kc) + return -EINVAL; + if (!name && (flags & CRYPT_ACTIVATE_REFRESH)) + return -EINVAL; + if ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd)) + return -EINVAL; + if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name) + return -EINVAL; + r = _check_header_data_overlap(cd, name); + if (r < 0) + return r; + r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH); + if (r < 0) + return r; + + /* for TCRYPT and token skip passphrase activation */ + if (kc->get_passphrase && kc->type != CRYPT_KC_TYPE_TOKEN && !isTCRYPT(cd->type)) { + r = kc->get_passphrase(cd, kc, &passphrase, &passphrase_size); + if (r < 0) + return r; + if (passphrase) { + if (isLOOPAES(cd->type)) + return _activate_loopaes(cd, name, passphrase, passphrase_size, flags); + else + return _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags); + } + } + /* only passphrase unlock is supported with loopaes */ + if (isLOOPAES(cd->type)) + return -EINVAL; + + /* activate by volume key */ + r = -EINVAL; + if (isLUKS1(cd->type)){ + if (kc->get_luks1_volume_key) + r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk); + } else if (isLUKS2(cd->type)) { + if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY && kc->get_luks2_key) + r = kc->get_luks2_key(cd, kc, keyslot, CRYPT_ANY_SEGMENT, &vk); + else if (kc->get_luks2_volume_key) + r = kc->get_luks2_volume_key(cd, kc, keyslot, &vk); + } else if (isTCRYPT(cd->type)) { + r = 0; + } else if (isPLAIN(cd->type)) { + r = kc->get_plain_volume_key(cd, kc, &vk); + } else if (isBITLK(cd->type)) { + r = kc->get_bitlk_volume_key(cd, kc, &vk); + } else if (isFVAULT2(cd->type)) { + r = kc->get_fvault2_volume_key(cd, kc, &vk); + } else if (isVERITY(cd->type)) { + r = kc->get_verity_volume_key(cd, kc, &vk); + } else if (isINTEGRITY(cd->type)) { + r = kc->get_integrity_volume_key(cd, kc, &vk); + } + if (r < 0 && (r != -ENOENT || kc->type == CRYPT_KC_TYPE_TOKEN)) + goto out; + unlocked_keyslot = r; + + if (r == -ENOENT) { + crypt_free_volume_key(vk); + vk = NULL; + } + + if ((r = _activate_by_volume_key(cd, name, vk ? vk->key : NULL, vk ? vk->keylength : 0, flags)) >= 0) + r = unlocked_keyslot >= 0 ? unlocked_keyslot : r; +out: + crypt_free_volume_key(vk); + return r; +} + +int crypt_activate_by_passphrase(struct crypt_device *cd, + const char *name, + int keyslot, + const char *passphrase, + size_t passphrase_size, + uint32_t flags) +{ + int r; + struct crypt_keyslot_context kc; + + crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); + r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, flags); + crypt_keyslot_context_destroy_internal(&kc); + + return r; +} + +int crypt_activate_by_keyfile_device_offset(struct crypt_device *cd, + const char *name, + int keyslot, + const char *keyfile, + size_t keyfile_size, + uint64_t keyfile_offset, + uint32_t flags) +{ + int r; + struct crypt_keyslot_context kc; + + crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset); + r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, flags); + crypt_keyslot_context_destroy_internal(&kc); + + return r; +} + +int crypt_activate_by_keyfile(struct crypt_device *cd, + const char *name, + int keyslot, + const char *keyfile, + size_t keyfile_size, + uint32_t flags) +{ + return crypt_activate_by_keyfile_device_offset(cd, name, keyslot, keyfile, + keyfile_size, 0, flags); +} + +int crypt_activate_by_keyfile_offset(struct crypt_device *cd, + const char *name, + int keyslot, + const char *keyfile, + size_t keyfile_size, + size_t keyfile_offset, + uint32_t flags) +{ + return crypt_activate_by_keyfile_device_offset(cd, name, keyslot, keyfile, + keyfile_size, keyfile_offset, flags); +} + +int crypt_activate_by_volume_key(struct crypt_device *cd, + const char *name, + const char *volume_key, + size_t volume_key_size, + uint32_t flags) +{ + int r; + struct crypt_keyslot_context kc; + + crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); + r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc, flags); + crypt_keyslot_context_destroy_internal(&kc); + + return r; +} + int crypt_activate_by_signed_key(struct crypt_device *cd, const char *name, const char *volume_key, @@ -6618,26 +6701,13 @@ int crypt_activate_by_token_pin(struct crypt_device *cd, const char *name, void *usrptr, uint32_t flags) { int r; + struct crypt_keyslot_context kc; - log_dbg(cd, "%s volume %s using token (%s type) %d.", - name ? "Activating" : "Checking", name ?: "passphrase", - type ?: "any", token); + crypt_keyslot_unlock_by_token_init_internal(&kc, token, type, pin, pin_size, usrptr); + r = crypt_activate_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc, flags); + crypt_keyslot_context_destroy_internal(&kc); - if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET | CRYPT_CD_UNRESTRICTED, 0))) - return r; - - if ((flags & CRYPT_ACTIVATE_KEYRING_KEY) && !crypt_use_keyring_for_vk(cd)) - return -EINVAL; - - if ((flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) && name) - return -EINVAL; - - r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH); - if (r < 0) - return r; - - return LUKS2_token_open_and_activate(cd, &cd->u.luks2.hdr, CRYPT_ANY_SLOT, token, - name, type, pin, pin_size, flags, usrptr); + return r; } int crypt_activate_by_token(struct crypt_device *cd,