diff -rupN cryptsetup-2.3.3.old/lib/libcryptsetup.h cryptsetup-2.3.3/lib/libcryptsetup.h --- cryptsetup-2.3.3.old/lib/libcryptsetup.h 2022-01-18 09:15:34.523672069 +0100 +++ cryptsetup-2.3.3/lib/libcryptsetup.h 2022-01-18 09:16:43.251047191 +0100 @@ -2194,6 +2194,8 @@ int crypt_activate_by_token(struct crypt #define CRYPT_REENCRYPT_RESUME_ONLY (1 << 2) /** Run reencryption recovery only. (in) */ #define CRYPT_REENCRYPT_RECOVERY (1 << 3) +/** Reencryption requires metadata protection. (in/out) */ +#define CRYPT_REENCRYPT_REPAIR_NEEDED (1 << 4) /** * Reencryption direction diff -rupN cryptsetup-2.3.3.old/lib/luks2/luks2.h cryptsetup-2.3.3/lib/luks2/luks2.h --- cryptsetup-2.3.3.old/lib/luks2/luks2.h 2022-01-18 09:15:34.520672053 +0100 +++ cryptsetup-2.3.3/lib/luks2/luks2.h 2022-01-18 09:16:43.252047196 +0100 @@ -561,6 +561,8 @@ int LUKS2_config_set_flags(struct crypt_ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t *reqs); int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs, bool commit); +int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint32_t *version); + int LUKS2_unmet_requirements(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t reqs_mask, int quiet); int LUKS2_key_description_by_segment(struct crypt_device *cd, diff -rupN cryptsetup-2.3.3.old/lib/luks2/luks2_json_metadata.c cryptsetup-2.3.3/lib/luks2/luks2_json_metadata.c --- cryptsetup-2.3.3.old/lib/luks2/luks2_json_metadata.c 2022-01-18 09:15:34.521672058 +0100 +++ cryptsetup-2.3.3/lib/luks2/luks2_json_metadata.c 2022-01-18 09:16:43.253047201 +0100 @@ -1469,6 +1469,49 @@ static const struct requirement_flag *ge return &unknown_requirement_flag; } +int LUKS2_config_get_reencrypt_version(struct luks2_hdr *hdr, uint32_t *version) +{ + json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + int i, len; + const struct requirement_flag *req; + + assert(hdr && version); + if (!hdr || !version) + return -EINVAL; + + if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) + return -EINVAL; + + if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) + return -ENOENT; + + if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory)) + return -ENOENT; + + len = (int) json_object_array_length(jobj_mandatory); + if (len <= 0) + return -ENOENT; + + for (i = 0; i < len; i++) { + jobj = json_object_array_get_idx(jobj_mandatory, i); + + /* search for requirements prefixed with "online-reencrypt" */ + if (strncmp(json_object_get_string(jobj), "online-reencrypt", 16)) + continue; + + /* check current library is aware of the requirement */ + req = get_requirement_by_name(json_object_get_string(jobj)); + if (req->flag == (uint32_t)CRYPT_REQUIREMENT_UNKNOWN) + continue; + + *version = req->version; + + return 0; + } + + return -ENOENT; +} + static const struct requirement_flag *stored_requirement_name_by_id(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t req_id) { json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; diff -rupN cryptsetup-2.3.3.old/lib/luks2/luks2_reencrypt.c cryptsetup-2.3.3/lib/luks2/luks2_reencrypt.c --- cryptsetup-2.3.3.old/lib/luks2/luks2_reencrypt.c 2022-01-18 09:15:34.520672053 +0100 +++ cryptsetup-2.3.3/lib/luks2/luks2_reencrypt.c 2022-01-18 09:25:26.870913236 +0100 @@ -2888,6 +2888,85 @@ static int reencrypt_recovery_by_passphr return r; } +static int reencrypt_repair_by_passphrase( + struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot_old, + int keyslot_new, + const char *passphrase, + size_t passphrase_size) +{ + int r; + struct crypt_lock_handle *reencrypt_lock; + struct luks2_reenc_context *rh; + crypt_reencrypt_info ri; + struct volume_key *vks = NULL; + + log_dbg(cd, "Loading LUKS2 reencryption context for metadata repair."); + + rh = crypt_get_reenc_context(cd); + if (rh) { + LUKS2_reenc_context_free(cd, rh); + crypt_set_reenc_context(cd, NULL); + rh = NULL; + } + + ri = LUKS2_reenc_status(hdr); + if (ri == CRYPT_REENCRYPT_INVALID) + return -EINVAL; + + if (ri < CRYPT_REENCRYPT_CLEAN) { + log_err(cd, _("Device is not in reencryption.")); + return -EINVAL; + } + + r = crypt_reencrypt_lock(cd, &reencrypt_lock); + if (r < 0) { + if (r == -EBUSY) + log_err(cd, _("Reencryption process is already running.")); + else + log_err(cd, _("Failed to acquire reencryption lock.")); + return r; + } + + /* With reencryption lock held, reload device context and verify metadata state */ + r = crypt_load(cd, CRYPT_LUKS2, NULL); + if (r) + goto out; + + ri = LUKS2_reenc_status(hdr); + if (ri == CRYPT_REENCRYPT_INVALID) { + r = -EINVAL; + goto out; + } + if (ri == CRYPT_REENCRYPT_NONE) { + r = 0; + goto out; + } + + r = LUKS2_keyslot_open_all_segments(cd, keyslot_old, keyslot_new, passphrase, passphrase_size, &vks); + if (r < 0) + goto out; + + r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, vks); + crypt_free_volume_key(vks); + vks = NULL; + if (r < 0) + goto out; + + /* removes online-reencrypt flag v1 */ + if ((r = reencrypt_update_flag(cd, 0, false))) + goto out; + + /* adds online-reencrypt flag v2 and commits metadata */ + r = reencrypt_update_flag(cd, 1, true); +out: + crypt_reencrypt_unlock(cd, reencrypt_lock); + crypt_free_volume_key(vks); + return r; + +} + static int reencrypt_init_by_passphrase(struct crypt_device *cd, const char *name, const char *passphrase, @@ -2904,6 +2983,10 @@ static int reencrypt_init_by_passphrase( uint32_t flags = params ? params->flags : 0; struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); + /* short-circuit in reencryption metadata update and finish immediately. */ + if (flags & CRYPT_REENCRYPT_REPAIR_NEEDED) + return reencrypt_repair_by_passphrase(cd, hdr, keyslot_old, keyslot_new, passphrase, passphrase_size); + /* short-circuit in recovery and finish immediately. */ if (flags & CRYPT_REENCRYPT_RECOVERY) return reencrypt_recovery_by_passphrase(cd, hdr, keyslot_old, keyslot_new, passphrase, passphrase_size); @@ -3459,12 +3542,28 @@ err: crypt_reencrypt_info LUKS2_reencrypt_status(struct crypt_device *cd, struct crypt_params_reencrypt *params) { crypt_reencrypt_info ri; + int digest; + uint32_t version; struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2); ri = LUKS2_reenc_status(hdr); if (ri == CRYPT_REENCRYPT_NONE || ri == CRYPT_REENCRYPT_INVALID || !params) return ri; + digest = LUKS2_digest_by_keyslot(hdr, LUKS2_find_keyslot(hdr, "reencrypt")); + if (digest < 0 && digest != -ENOENT) + return CRYPT_REENCRYPT_INVALID; + + /* + * In case there's an old "online-reencrypt" requirement or reencryption + * keyslot digest is missing inform caller reencryption metadata requires repair. + */ + if (!LUKS2_config_get_reencrypt_version(hdr, &version) && + (version < 2 || digest == -ENOENT)) { + params->flags |= CRYPT_REENCRYPT_REPAIR_NEEDED; + return ri; + } + params->mode = reencrypt_mode(hdr); params->direction = reencrypt_direction(hdr); params->resilience = reencrypt_resilience_type(hdr);