From aea21309edb3a82b73b4088f54f3b45cb03b187b Mon Sep 17 00:00:00 2001 From: Daniel Zatovic Date: Thu, 20 Apr 2023 18:37:56 +0200 Subject: [PATCH] Add keyring keyslot_context. --- lib/keyslot_context.c | 131 ++++++++++++++++++++++++++++++++++++++++++ lib/keyslot_context.h | 6 ++ lib/libcryptsetup.h | 19 ++++++ lib/libcryptsetup.sym | 1 + lib/setup.c | 35 ++++------- tests/api-test-2.c | 14 +++++ 6 files changed, 181 insertions(+), 25 deletions(-) diff --git a/lib/keyslot_context.c b/lib/keyslot_context.c index ab1ab320..01294aaf 100644 --- a/lib/keyslot_context.c +++ b/lib/keyslot_context.c @@ -273,6 +273,95 @@ static int get_passphrase_by_token(struct crypt_device *cd, return kc->u.t.id; } +static int get_passphrase_by_keyring(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + const char **r_passphrase, + size_t *r_passphrase_size) +{ + int r; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_KEYRING); + assert(r_passphrase); + assert(r_passphrase_size); + + if (!kc->i_passphrase) { + r = keyring_get_passphrase(kc->u.kr.key_description, &kc->i_passphrase, &kc->i_passphrase_size); + if (r < 0) { + log_err(cd, _("Failed to read passphrase from keyring.")); + kc->error = -EINVAL; + return -EINVAL; + } + } + + *r_passphrase = kc->i_passphrase; + *r_passphrase_size = kc->i_passphrase_size; + + return 0; +} + +static int get_luks2_key_by_keyring(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + int keyslot, + int segment, + struct volume_key **r_vk) +{ + int r; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_KEYRING); + assert(r_vk); + + r = get_passphrase_by_keyring(cd, kc, CONST_CAST(const char **) &kc->i_passphrase, + &kc->i_passphrase_size); + if (r < 0) { + log_err(cd, _("Failed to read passphrase from keyring.")); + kc->error = -EINVAL; + return -EINVAL; + } + + r = LUKS2_keyslot_open(cd, keyslot, segment, kc->i_passphrase, kc->i_passphrase_size, r_vk); + if (r < 0) + kc->error = r; + + return 0; +} + +static int get_luks2_volume_key_by_keyring(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + int keyslot, + struct volume_key **r_vk) +{ + return get_luks2_key_by_passphrase(cd, kc, keyslot, CRYPT_DEFAULT_SEGMENT, r_vk); +} + +static int get_luks1_volume_key_by_keyring(struct crypt_device *cd, + struct crypt_keyslot_context *kc, + int keyslot, + struct volume_key **r_vk) +{ + int r; + + assert(cd); + assert(kc && kc->type == CRYPT_KC_TYPE_PASSPHRASE); + assert(r_vk); + + r = get_passphrase_by_keyring(cd, kc, CONST_CAST(const char **) &kc->i_passphrase, + &kc->i_passphrase_size); + if (r < 0) { + log_err(cd, _("Failed to read passphrase from keyring.")); + kc->error = -EINVAL; + return -EINVAL; + } + + r = LUKS_open_key_with_hdr(keyslot, kc->i_passphrase, kc->i_passphrase_size, + crypt_get_hdr(cd, CRYPT_LUKS1), r_vk, cd); + if (r < 0) + kc->error = r; + + return r; +} + static void unlock_method_init_internal(struct crypt_keyslot_context *kc) { assert(kc); @@ -282,6 +371,26 @@ static void unlock_method_init_internal(struct crypt_keyslot_context *kc) kc->i_passphrase_size = 0; } +void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc, + const char *key_description) +{ + assert(kc); + + kc->type = CRYPT_KC_TYPE_KEYRING; + kc->u.kr.key_description = key_description; + + kc->get_luks2_key = get_luks2_key_by_keyring; + kc->get_luks2_volume_key = get_luks2_volume_key_by_keyring; + kc->get_luks1_volume_key = get_luks1_volume_key_by_keyring; + kc->get_passphrase = get_passphrase_by_keyring; + 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); +} + void crypt_keyslot_unlock_by_key_init_internal(struct crypt_keyslot_context *kc, const char *volume_key, size_t volume_key_size) @@ -477,6 +586,26 @@ int crypt_keyslot_context_init_by_volume_key(struct crypt_device *cd, return 0; } +int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd, + const char *key_description, + 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_keyring_internal(tmp, key_description); + + *kc = tmp; + + return 0; +} + int crypt_keyslot_context_get_error(struct crypt_keyslot_context *kc) { return kc ? kc->error : -EINVAL; @@ -514,6 +643,8 @@ const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc) return "token"; case CRYPT_KC_TYPE_KEY: return "key"; + case CRYPT_KC_TYPE_KEYRING: + return "keyring"; default: return ""; } diff --git a/lib/keyslot_context.h b/lib/keyslot_context.h index 6e7e406f..d05183a9 100644 --- a/lib/keyslot_context.h +++ b/lib/keyslot_context.h @@ -76,6 +76,9 @@ struct crypt_keyslot_context { const char *volume_key; size_t volume_key_size; } k; + struct { + const char *key_description; + } kr; } u; int error; @@ -116,6 +119,9 @@ void crypt_keyslot_unlock_by_token_init_internal(struct crypt_keyslot_context *k size_t pin_size, void *usrptr); +void crypt_keyslot_unlock_by_keyring_internal(struct crypt_keyslot_context *kc, + const char *key_description); + const char *keyslot_context_type_string(const struct crypt_keyslot_context *kc); #endif /* KEYSLOT_CONTEXT_H */ diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index d3222353..b195e3f4 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -1262,6 +1262,21 @@ 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 passphrase stored in a keyring. + * + * @param cd crypt device handle initialized to LUKS device context + * + * @param key_description kernel keyring key description library should look + * for passphrase in + * @param kc returns crypt keyslot context handle type CRYPT_KC_TYPE_KEYRING + * + * @return zero on success or negative errno otherwise. + */ +int crypt_keyslot_context_init_by_keyring(struct crypt_device *cd, + const char *key_description, + struct crypt_keyslot_context **kc); + /** * Get error code per keyslot context from last failed call. * @@ -1305,6 +1320,10 @@ int crypt_keyslot_context_set_pin(struct crypt_device *cd, #define CRYPT_KC_TYPE_TOKEN INT16_C(3) /** keyslot context initialized by volume key or unbound key (@link crypt_keyslot_context_init_by_volume_key @endlink) */ #define CRYPT_KC_TYPE_KEY INT16_C(4) +/** keyslot context initialized by description of a keyring key + * (@link crypt_keyslot_context_init_by_keyring @endlink) + */ +#define CRYPT_KC_TYPE_KEYRING INT16_C(5) /** @} */ /** diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index a4fce23a..ee94b1f3 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -172,5 +172,6 @@ CRYPTSETUP_2.7 { crypt_format_luks2_opal; crypt_get_hw_encryption_type; crypt_get_hw_encryption_key_size; + crypt_keyslot_context_init_by_keyring; crypt_wipe_hw_opal; } CRYPTSETUP_2.6; diff --git a/lib/setup.c b/lib/setup.c index 6ba247ed..cae72400 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -5289,8 +5289,8 @@ int crypt_activate_by_keyslot_context(struct crypt_device *cd, 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)); + log_dbg(cd, "%s volume %s [keyslot %d] using %s.", + name ? "Activating" : "Checking", name ?: "passphrase", keyslot, keyslot_context_type_string(kc)); if (!cd || !kc) return -EINVAL; @@ -5300,6 +5300,10 @@ 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()) { + log_err(cd, _("Kernel keyring is not supported by the kernel.")); + return -EINVAL; + } r = _check_header_data_overlap(cd, name); if (r < 0) return r; @@ -7322,34 +7326,15 @@ int crypt_activate_by_keyring(struct crypt_device *cd, int keyslot, uint32_t flags) { - char *passphrase; - size_t passphrase_size; int r; + struct crypt_keyslot_context kc; if (!cd || !key_description) return -EINVAL; - log_dbg(cd, "%s volume %s [keyslot %d] using passphrase in keyring.", - name ? "Activating" : "Checking", name ?: "passphrase", keyslot); - - if (!kernel_keyring_support()) { - log_err(cd, _("Kernel keyring is not supported by the kernel.")); - return -EINVAL; - } - - r = _activate_check_status(cd, name, flags & CRYPT_ACTIVATE_REFRESH); - if (r < 0) - return r; - - r = keyring_get_passphrase(key_description, &passphrase, &passphrase_size); - if (r < 0) { - log_err(cd, _("Failed to read passphrase from keyring (error %d)."), r); - return -EINVAL; - } - - r = _activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags); - - crypt_safe_free(passphrase); + crypt_keyslot_unlock_by_keyring_internal(&kc, key_description); + r = crypt_activate_by_keyslot_context(cd, name, keyslot, &kc, flags); + crypt_keyslot_context_destroy_internal(&kc); return r; } diff --git a/tests/api-test-2.c b/tests/api-test-2.c index aa0ceba1..f1dbcb56 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -5128,6 +5128,7 @@ static void KeyslotContext(void) uint64_t r_payload_offset; char key[128]; size_t key_size = 128; + key_serial_t kid; OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset)); OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1)); @@ -5140,6 +5141,9 @@ static void KeyslotContext(void) EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 32, KEY1, strlen(KEY1)), 1); EQ_(0, crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key, &key_size, NULL, 0)); + kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING); + NOTFAIL_(kid, "Test or kernel keyring are broken."); + // test passphrase OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &kc)); EQ_(crypt_activate_by_keyslot_context(cd, NULL, CRYPT_ANY_SLOT, kc, 0), 0); @@ -5158,6 +5162,10 @@ static void KeyslotContext(void) EQ_(crypt_activate_by_keyslot_context(cd, NULL, CRYPT_ANY_SLOT, kc, 0), 1); crypt_keyslot_context_free(kc); + OK_(crypt_keyslot_context_init_by_keyring(cd, KEY_DESC_TEST0, &kc)); + EQ_(crypt_activate_by_keyslot_context(cd, NULL, CRYPT_ANY_SLOT, kc, 0), 0); + crypt_keyslot_context_free(kc); + // test activation OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &kc)); EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); @@ -5177,6 +5185,12 @@ static void KeyslotContext(void) OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_keyslot_context_free(kc); + OK_(crypt_keyslot_context_init_by_keyring(cd, KEY_DESC_TEST0, &kc)); + EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); + OK_(crypt_deactivate(cd, CDEVICE_1)); + crypt_keyslot_context_free(kc); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); CRYPT_FREE(cd); _cleanup_dmdevices(); }