Blob Blame History Raw
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);