diff --git a/lib/keyslot_context.c b/lib/keyslot_context.c index 01294aaf..b5566157 100644 --- a/lib/keyslot_context.c +++ b/lib/keyslot_context.c @@ -332,7 +332,7 @@ static int get_luks2_volume_key_by_keyring(struct crypt_device *cd, int keyslot, struct volume_key **r_vk) { - return get_luks2_key_by_passphrase(cd, kc, keyslot, CRYPT_DEFAULT_SEGMENT, r_vk); + return get_luks2_key_by_keyring(cd, kc, keyslot, CRYPT_DEFAULT_SEGMENT, r_vk); } static int get_luks1_volume_key_by_keyring(struct crypt_device *cd, diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index b195e3f4..ef2ac2cb 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -1009,6 +1009,23 @@ int crypt_resume_by_token_pin(struct crypt_device *cd, const char *pin, size_t pin_size, void *usrptr); + +/** + * Resume crypt device using keyslot context. + * + * @param cd crypt device handle + * @param name name of device to resume + * @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. + * + * @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_resume_by_keyslot_context(struct crypt_device *cd, + const char *name, + int keyslot, + struct crypt_keyslot_context *kc); /** @} */ /** diff --git a/lib/libcryptsetup.sym b/lib/libcryptsetup.sym index ee94b1f3..d907ce9a 100644 --- a/lib/libcryptsetup.sym +++ b/lib/libcryptsetup.sym @@ -173,5 +173,6 @@ CRYPTSETUP_2.7 { crypt_get_hw_encryption_type; crypt_get_hw_encryption_key_size; crypt_keyslot_context_init_by_keyring; + crypt_resume_by_keyslot_context; crypt_wipe_hw_opal; } CRYPTSETUP_2.6; diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c index 98f0f55a..c7795b73 100644 --- a/lib/luks2/luks2_keyslot.c +++ b/lib/luks2/luks2_keyslot.c @@ -578,6 +578,8 @@ int LUKS2_keyslot_open(struct crypt_device *cd, int r_prio, r = -EINVAL; hdr = crypt_get_hdr(cd, CRYPT_LUKS2); + if (!hdr) + return -EINVAL; if (keyslot == CRYPT_ANY_SLOT) { r_prio = LUKS2_keyslot_open_priority(cd, hdr, CRYPT_SLOT_PRIORITY_PREFER, diff --git a/lib/setup.c b/lib/setup.c index cae72400..820fc0d6 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -4078,21 +4078,19 @@ static int resume_by_volume_key(struct crypt_device *cd, return -EINVAL; } -int crypt_resume_by_passphrase(struct crypt_device *cd, +int crypt_resume_by_keyslot_context(struct crypt_device *cd, const char *name, int keyslot, - const char *passphrase, - size_t passphrase_size) + struct crypt_keyslot_context *kc) { - struct volume_key *vk = NULL; int r; + struct volume_key *vk = NULL; + int unlocked_keyslot = -EINVAL; - /* FIXME: check context uuid matches the dm-crypt device uuid */ - - if (!passphrase || !name) + if (!name) return -EINVAL; - log_dbg(cd, "Resuming volume %s.", name); + log_dbg(cd, "Resuming volume %s [keyslot %d] using %s.", name, keyslot, keyslot_context_type_string(kc)); if ((r = onlyLUKS(cd))) return r; @@ -4106,24 +4104,50 @@ int crypt_resume_by_passphrase(struct crypt_device *cd, return -EINVAL; } + if (isLUKS1(cd->type) && kc->get_luks1_volume_key) + r = kc->get_luks1_volume_key(cd, kc, keyslot, &vk); + else if (isLUKS2(cd->type) && kc->get_luks2_volume_key) + r = kc->get_luks2_volume_key(cd, kc, keyslot, &vk); + else + r = -EINVAL; + if (r < 0) + goto out; + unlocked_keyslot = r; + if (isLUKS1(cd->type)) { - r = LUKS_open_key_with_hdr(keyslot, passphrase, passphrase_size, - &cd->u.luks1.hdr, &vk, cd); - if (r >= 0) - crypt_volume_key_set_id(vk, 0); - + r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk); + crypt_volume_key_set_id(vk, 0); + } else if (isLUKS2(cd->type)) { + r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); + crypt_volume_key_set_id(vk, 0); } else - r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase, passphrase_size, &vk); - - if (r < 0) - return r; - - keyslot = r; + r = -EINVAL; + if (r < 0) + goto out; r = resume_by_volume_key(cd, vk, name); crypt_free_volume_key(vk); - return r < 0 ? r : keyslot; + return r < 0 ? r : unlocked_keyslot; +out: + crypt_free_volume_key(vk); + return r; +} + +int crypt_resume_by_passphrase(struct crypt_device *cd, + const char *name, + int keyslot, + const char *passphrase, + size_t passphrase_size) +{ + int r; + struct crypt_keyslot_context kc; + + crypt_keyslot_unlock_by_passphrase_init_internal(&kc, passphrase, passphrase_size); + r = crypt_resume_by_keyslot_context(cd, name, keyslot, &kc); + crypt_keyslot_context_destroy_internal(&kc); + + return r; } int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd, @@ -4133,55 +4157,14 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd, size_t keyfile_size, uint64_t keyfile_offset) { - struct volume_key *vk = NULL; - char *passphrase_read = NULL; - size_t passphrase_size_read; int r; + struct crypt_keyslot_context kc; - /* FIXME: check context uuid matches the dm-crypt device uuid */ + crypt_keyslot_unlock_by_keyfile_init_internal(&kc, keyfile, keyfile_size, keyfile_offset); + r = crypt_resume_by_keyslot_context(cd, name, keyslot, &kc); + crypt_keyslot_context_destroy_internal(&kc); - if (!name || !keyfile) - return -EINVAL; - - log_dbg(cd, "Resuming volume %s.", name); - - if ((r = onlyLUKS(cd))) - return r; - - r = dm_status_suspended(cd, name); - if (r < 0) - return r; - - if (!r) { - log_err(cd, _("Volume %s is not suspended."), name); - return -EINVAL; - } - - r = crypt_keyfile_device_read(cd, keyfile, - &passphrase_read, &passphrase_size_read, - keyfile_offset, keyfile_size, 0); - if (r < 0) - return r; - - if (isLUKS1(cd->type)) { - r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read, - &cd->u.luks1.hdr, &vk, cd); - if (r >= 0) - crypt_volume_key_set_id(vk, 0); - } else - r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, - passphrase_read, passphrase_size_read, &vk); - - crypt_safe_free(passphrase_read); - if (r < 0) - return r; - - keyslot = r; - - r = resume_by_volume_key(cd, vk, name); - - crypt_free_volume_key(vk); - return r < 0 ? r : keyslot; + return r; } int crypt_resume_by_keyfile(struct crypt_device *cd, @@ -4210,46 +4193,16 @@ int crypt_resume_by_volume_key(struct crypt_device *cd, const char *volume_key, size_t volume_key_size) { - struct volume_key *vk = NULL; int r; + struct crypt_keyslot_context kc; - if (!name || !volume_key) - return -EINVAL; + crypt_keyslot_unlock_by_key_init_internal(&kc, volume_key, volume_key_size); + r = crypt_resume_by_keyslot_context(cd, name, CRYPT_ANY_SLOT /* unused */, &kc); + crypt_keyslot_context_destroy_internal(&kc); - log_dbg(cd, "Resuming volume %s by volume key.", name); - - if ((r = onlyLUKS(cd))) - return r; - - r = dm_status_suspended(cd, name); - if (r < 0) - return r; - - if (!r) { - log_err(cd, _("Volume %s is not suspended."), name); - return -EINVAL; - } - - vk = crypt_alloc_volume_key(volume_key_size, volume_key); - if (!vk) - return -ENOMEM; - - if (isLUKS1(cd->type)) { - r = LUKS_verify_volume_key(&cd->u.luks1.hdr, vk); - if (r >= 0) - crypt_volume_key_set_id(vk, 0); - } else if (isLUKS2(cd->type)) { - r = LUKS2_digest_verify_by_segment(cd, &cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT, vk); - crypt_volume_key_set_id(vk, r); - } else - r = -EINVAL; if (r == -EPERM || r == -ENOENT) log_err(cd, _("Volume key does not match the volume.")); - if (r >= 0) - r = resume_by_volume_key(cd, vk, name); - - crypt_free_volume_key(vk); return r; } @@ -4257,35 +4210,14 @@ int crypt_resume_by_token_pin(struct crypt_device *cd, const char *name, const char *type, int token, const char *pin, size_t pin_size, void *usrptr) { - struct volume_key *vk = NULL; - int r, keyslot; + int r; + struct crypt_keyslot_context kc; - if (!name) - return -EINVAL; + crypt_keyslot_unlock_by_token_init_internal(&kc, token, type, pin, pin_size, usrptr); + r = crypt_resume_by_keyslot_context(cd, name, CRYPT_ANY_SLOT, &kc); + crypt_keyslot_context_destroy_internal(&kc); - log_dbg(cd, "Resuming volume %s by token (%s type) %d.", - name, type ?: "any", token); - - if ((r = _onlyLUKS2(cd, CRYPT_CD_QUIET, 0))) - return r; - - r = dm_status_suspended(cd, name); - if (r < 0) - return r; - - if (!r) { - log_err(cd, _("Volume %s is not suspended."), name); - return -EINVAL; - } - - r = LUKS2_token_unlock_key(cd, &cd->u.luks2.hdr, CRYPT_ANY_SLOT, token, type, - pin, pin_size, CRYPT_DEFAULT_SEGMENT, usrptr, &vk); - keyslot = r; - if (r >= 0) - r = resume_by_volume_key(cd, vk, name); - - crypt_free_volume_key(vk); - return r < 0 ? r : keyslot; + return r; } /* diff --git a/tests/api-test-2.c b/tests/api-test-2.c index f1dbcb56..e8563425 100644 --- a/tests/api-test-2.c +++ b/tests/api-test-2.c @@ -5129,6 +5129,8 @@ static void KeyslotContext(void) char key[128]; size_t key_size = 128; key_serial_t kid; + int suspend_status; + struct crypt_active_device cad; OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset)); OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1)); @@ -5190,6 +5192,51 @@ static void KeyslotContext(void) OK_(crypt_deactivate(cd, CDEVICE_1)); crypt_keyslot_context_free(kc); + // test resume + 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); + suspend_status = crypt_suspend(cd, CDEVICE_1); + if (suspend_status == -ENOTSUP) { + printf("WARNING: Suspend/Resume not supported, skipping test.\n"); + OK_(crypt_deactivate(cd, CDEVICE_1)); + + NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken."); + CRYPT_FREE(cd); + _cleanup_dmdevices(); + return; + } + OK_(suspend_status); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(CRYPT_ACTIVATE_SUSPENDED, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + OK_(crypt_resume_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(0, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + OK_(crypt_deactivate(cd, CDEVICE_1)); + crypt_keyslot_context_free(kc); + + OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &kc)); + EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 0); + OK_(crypt_suspend(cd, CDEVICE_1)); + EQ_(crypt_resume_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc), 0); + OK_(crypt_deactivate(cd, CDEVICE_1)); + crypt_keyslot_context_free(kc); + + OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &kc)); + EQ_(crypt_activate_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc, 0), 1); + OK_(crypt_suspend(cd, CDEVICE_1)); + OK_(crypt_get_active_device(cd, CDEVICE_1, &cad)); + EQ_(CRYPT_ACTIVATE_SUSPENDED, cad.flags & CRYPT_ACTIVATE_SUSPENDED); + EQ_(crypt_resume_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc), 1); + 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_suspend(cd, CDEVICE_1)); + EQ_(crypt_resume_by_keyslot_context(cd, CDEVICE_1, CRYPT_ANY_SLOT, kc), 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();