Add CRYPT_REENCRYPT_REPAIR_NEEDED flag.

crypt_reencrypt_status() returns this flag if old
online-reencrypt requirement is detected and reencryption
keyslot digest is missing.

crypt_reencrypt_init_by_passphrase() with same flag applied
repairs (upgrade) reencryption metadata so that
automatic reencryption recovery during activation
is again possible and reencryption operation can be resumed
post CVE-2021-4122 fix.
This commit is contained in:
Ondrej Kozina
2022-01-11 16:59:26 +01:00
committed by Milan Broz
parent 2e44267891
commit 624f3220a1
4 changed files with 146 additions and 0 deletions

View File

@@ -2204,6 +2204,8 @@ int crypt_activate_by_token(struct crypt_device *cd,
#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

View File

@@ -398,6 +398,8 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3
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,

View File

@@ -1486,6 +1486,49 @@ static const struct requirement_flag *get_requirement_by_name(const char *requir
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;

View File

@@ -3000,6 +3000,85 @@ static int reencrypt_recovery_by_passphrase(struct crypt_device *cd,
LUKS2_reencrypt_unlock(cd, reencrypt_lock);
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_reencrypt *rh;
crypt_reencrypt_info ri;
struct volume_key *vks = NULL;
log_dbg(cd, "Loading LUKS2 reencryption context for metadata repair.");
rh = crypt_get_luks2_reencrypt(cd);
if (rh) {
LUKS2_reencrypt_free(cd, rh);
crypt_set_luks2_reencrypt(cd, NULL);
rh = NULL;
}
ri = LUKS2_reencrypt_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 = LUKS2_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_reencrypt_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:
LUKS2_reencrypt_unlock(cd, reencrypt_lock);
crypt_free_volume_key(vks);
return r;
}
#endif
static int reencrypt_init_by_passphrase(struct crypt_device *cd,
const char *name,
@@ -3018,6 +3097,10 @@ static int reencrypt_init_by_passphrase(struct crypt_device *cd,
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);
@@ -3586,11 +3669,27 @@ crypt_reencrypt_info LUKS2_reencrypt_get_params(struct luks2_hdr *hdr,
struct crypt_params_reencrypt *params)
{
crypt_reencrypt_info ri;
int digest;
uint32_t version;
ri = LUKS2_reencrypt_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);