diff --git a/0006-CVE-2021-4122.patch b/0006-CVE-2021-4122.patch new file mode 100644 index 0000000000000000000000000000000000000000..95b85e7b75b9867c283807004d4d8ac6c682b290 --- /dev/null +++ b/0006-CVE-2021-4122.patch @@ -0,0 +1,855 @@ +From 60addcffa6794c29dccf33d8db5347f24b75f2fc Mon Sep 17 00:00:00 2001 +From: Ondrej Kozina +Date: Sun, 2 Jan 2022 16:57:31 +0100 +Subject: [PATCH] Fix CVE-2021-4122 - LUKS2 reencryption crash recovery attack + +Fix possible attacks against data confidentiality through LUKS2 online +reencryption extension crash recovery. + +An attacker can modify on-disk metadata to simulate decryption in +progress with crashed (unfinished) reencryption step and persistently +decrypt part of the LUKS device. + +This attack requires repeated physical access to the LUKS device but +no knowledge of user passphrases. + +The decryption step is performed after a valid user activates +the device with a correct passphrase and modified metadata. +There are no visible warnings for the user that such recovery happened +(except using the luksDump command). The attack can also be reversed +afterward (simulating crashed encryption from a plaintext) with +possible modification of revealed plaintext. + +The problem was caused by reusing a mechanism designed for actual +reencryption operation without reassessing the security impact for new +encryption and decryption operations. While the reencryption requires +calculating and verifying both key digests, no digest was needed to +initiate decryption recovery if the destination is plaintext (no +encryption key). Also, some metadata (like encryption cipher) is not +protected, and an attacker could change it. Note that LUKS2 protects +visible metadata only when a random change occurs. It does not protect +against intentional modification but such modification must not cause +a violation of data confidentiality. + +The fix introduces additional digest protection of reencryption +metadata. The digest is calculated from known keys and critical +reencryption metadata. Now an attacker cannot create correct metadata +digest without knowledge of a passphrase for used keyslots. +For more details, see LUKS2 On-Disk Format Specification version 1.1.0. + +Conflict: move LUKS2_keyslot_reencrypt_digest_create to lib/luks2/luks2.h + move LUKS2_keyslot_jobj_area to lib/luks2/luks2.h + reencrypt_keyslot_update -> reenc_keyslot_update + +Signed-off-by: yanglongkang +--- + lib/Makemodule.am | 1 + + lib/luks2/luks2.h | 10 + + lib/luks2/luks2_json_metadata.c | 73 +++++- + lib/luks2/luks2_keyslot.c | 31 ++- + lib/luks2/luks2_keyslot_reenc.c | 18 +- + lib/luks2/luks2_reencrypt.c | 40 ++- + lib/luks2/luks2_reencrypt_digest.c | 381 +++++++++++++++++++++++++++++ + lib/setup.c | 21 +- + po/POTFILES.in | 1 + + 9 files changed, 533 insertions(+), 43 deletions(-) + create mode 100644 lib/luks2/luks2_reencrypt_digest.c + +diff --git a/lib/Makemodule.am b/lib/Makemodule.am +index bf4230b..69159fa 100644 +--- a/lib/Makemodule.am ++++ b/lib/Makemodule.am +@@ -104,6 +104,7 @@ libcryptsetup_la_SOURCES = \ + lib/luks2/luks2_keyslot_luks2.c \ + lib/luks2/luks2_keyslot_reenc.c \ + lib/luks2/luks2_reencrypt.c \ ++ lib/luks2/luks2_reencrypt_digest.c \ + lib/luks2/luks2_segment.c \ + lib/luks2/luks2_token_keyring.c \ + lib/luks2/luks2_token.c \ +diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h +index 6ab753a..610917b 100644 +--- a/lib/luks2/luks2.h ++++ b/lib/luks2/luks2.h +@@ -297,9 +297,15 @@ int LUKS2_keyslot_wipe(struct crypt_device *cd, + int keyslot, + int wipe_area_only); + ++int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks); ++ + int LUKS2_keyslot_dump(struct crypt_device *cd, + int keyslot); + ++int LUKS2_keyslot_jobj_area(json_object *jobj_keyslot, uint64_t *offset, uint64_t *length); ++ + crypt_keyslot_priority LUKS2_keyslot_priority_get(struct crypt_device *cd, + struct luks2_hdr *hdr, + int keyslot); +@@ -606,4 +612,8 @@ void crypt_reencrypt_unlock(struct crypt_device *cd, struct crypt_lock_handle *r + + int luks2_check_device_size(struct crypt_device *cd, struct luks2_hdr *hdr, uint64_t check_size, uint64_t *dev_size, bool activation, bool dynamic); + ++int LUKS2_reencrypt_digest_verify(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks); ++ + #endif +diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c +index e4e1424..fb6cef5 100644 +--- a/lib/luks2/luks2_json_metadata.c ++++ b/lib/luks2/luks2_json_metadata.c +@@ -1368,24 +1368,63 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3 + */ + + /* LUKS2 library requirements */ +-static const struct { ++struct requirement_flag { + uint32_t flag; ++ uint32_t version; + const char *description; +-} requirements_flags[] = { +- { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT, "offline-reencrypt" }, +- { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, "online-reencrypt" }, +- { 0, NULL } + }; + +-static uint32_t get_requirement_by_name(const char *requirement) ++static const struct requirement_flag unknown_requirement_flag = { CRYPT_REQUIREMENT_UNKNOWN, 0, NULL }; ++ ++static const struct requirement_flag requirements_flags[] = { ++ { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT,1, "offline-reencrypt" }, ++ { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 2, "online-reencrypt-v2" }, ++ { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1, "online-reencrypt" }, ++ { 0, 0, NULL } ++}; ++ ++static const struct requirement_flag *get_requirement_by_name(const char *requirement) + { + int i; + + for (i = 0; requirements_flags[i].description; i++) + if (!strcmp(requirement, requirements_flags[i].description)) +- return requirements_flags[i].flag; ++ return requirements_flags + i; + +- return CRYPT_REQUIREMENT_UNKNOWN; ++ return &unknown_requirement_flag; ++} ++ ++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; ++ int i, len; ++ const struct requirement_flag *req; ++ ++ assert(hdr); ++ if (!hdr) ++ return NULL; ++ ++ if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) ++ return NULL; ++ ++ if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) ++ return NULL; ++ ++ if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory)) ++ return NULL; ++ ++ len = (int) json_object_array_length(jobj_mandatory); ++ if (len <= 0) ++ return 0; ++ ++ for (i = 0; i < len; i++) { ++ jobj = json_object_array_get_idx(jobj_mandatory, i); ++ req = get_requirement_by_name(json_object_get_string(jobj)); ++ if (req->flag == req_id) ++ return req; ++ } ++ ++ return NULL; + } + + /* +@@ -1395,7 +1434,7 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + { + json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + int i, len; +- uint32_t req; ++ const struct requirement_flag *req; + + assert(hdr); + if (!hdr || !reqs) +@@ -1422,8 +1461,8 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + jobj = json_object_array_get_idx(jobj_mandatory, i); + req = get_requirement_by_name(json_object_get_string(jobj)); + log_dbg(cd, "%s - %sknown", json_object_get_string(jobj), +- reqs_unknown(req) ? "un" : ""); +- *reqs |= req; ++ reqs_unknown(req->flag) ? "un" : ""); ++ *reqs |= req->flag; + } + + return 0; +@@ -1433,6 +1472,8 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + { + json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + int i, r = -EINVAL; ++ const struct requirement_flag *req; ++ uint32_t req_id; + + if (!hdr) + return -EINVAL; +@@ -1442,8 +1483,14 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + return -ENOMEM; + + for (i = 0; requirements_flags[i].description; i++) { +- if (reqs & requirements_flags[i].flag) { +- jobj = json_object_new_string(requirements_flags[i].description); ++ req_id = reqs & requirements_flags[i].flag; ++ if (req_id) { ++ /* retain already stored version of requirement flag */ ++ req = stored_requirement_name_by_id(cd, hdr, req_id); ++ if (req) ++ jobj = json_object_new_string(req->description); ++ else ++ jobj = json_object_new_string(requirements_flags[i].description); + if (!jobj) { + r = -ENOMEM; + goto err; +diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c +index 3b8c889..2435a1f 100644 +--- a/lib/luks2/luks2_keyslot.c ++++ b/lib/luks2/luks2_keyslot.c +@@ -281,19 +281,9 @@ crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot) + return CRYPT_SLOT_ACTIVE; + } + +-int LUKS2_keyslot_area(struct luks2_hdr *hdr, +- int keyslot, +- uint64_t *offset, +- uint64_t *length) ++int LUKS2_keyslot_jobj_area(json_object *jobj_keyslot, uint64_t *offset, uint64_t *length) + { +- json_object *jobj_keyslot, *jobj_area, *jobj; +- +- if(LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID) +- return -EINVAL; +- +- jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); +- if (!jobj_keyslot) +- return -ENOENT; ++ json_object *jobj_area, *jobj; + + if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) + return -EINVAL; +@@ -309,6 +299,23 @@ int LUKS2_keyslot_area(struct luks2_hdr *hdr, + return 0; + } + ++int LUKS2_keyslot_area(struct luks2_hdr *hdr, ++ int keyslot, ++ uint64_t *offset, ++ uint64_t *length) ++{ ++ json_object *jobj_keyslot; ++ ++ if (LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID) ++ return -EINVAL; ++ ++ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); ++ if (!jobj_keyslot) ++ return -ENOENT; ++ ++ return LUKS2_keyslot_jobj_area(jobj_keyslot, offset, length); ++} ++ + static int _open_and_verify(struct crypt_device *cd, + struct luks2_hdr *hdr, + const keyslot_handler *h, +diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c +index b939467..854c926 100644 +--- a/lib/luks2/luks2_keyslot_reenc.c ++++ b/lib/luks2/luks2_keyslot_reenc.c +@@ -179,6 +179,7 @@ static int reenc_keyslot_store(struct crypt_device *cd, + int reenc_keyslot_update(struct crypt_device *cd, + const struct luks2_reenc_context *rh) + { ++ int r; + json_object *jobj_keyslot, *jobj_area, *jobj_area_type; + struct luks2_hdr *hdr; + +@@ -208,11 +209,24 @@ int reenc_keyslot_update(struct crypt_device *cd, + } else + log_dbg(cd, "No update of reencrypt keyslot needed."); + +- return 0; ++ r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, rh->vks); ++ if (r < 0) ++ log_err(cd, "Failed to refresh reencryption verification digest."); ++ ++ return r; + } + +-static int reenc_keyslot_wipe(struct crypt_device *cd, int keyslot) ++static int reenc_keyslot_wipe(struct crypt_device *cd, ++ int keyslot) + { ++ struct luks2_hdr *hdr; ++ ++ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) ++ return -EINVAL; ++ ++ /* remove reencryption verification data */ ++ LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0); ++ + return 0; + } + +diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c +index 68d3194..d170f09 100644 +--- a/lib/luks2/luks2_reencrypt.c ++++ b/lib/luks2/luks2_reencrypt.c +@@ -2390,6 +2390,10 @@ static int reencrypt_init(struct crypt_device *cd, + if (r < 0) + goto err; + ++ r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, *vks); ++ if (r < 0) ++ goto err; ++ + if (name && params->mode != CRYPT_REENCRYPT_ENCRYPT) { + r = reencrypt_verify_and_upload_keys(cd, hdr, LUKS2_reencrypt_digest_old(hdr), LUKS2_reencrypt_digest_new(hdr), *vks); + if (r) +@@ -2520,20 +2524,28 @@ static int reencrypt_context_update(struct crypt_device *cd, + static int reencrypt_load(struct crypt_device *cd, struct luks2_hdr *hdr, + uint64_t device_size, + const struct crypt_params_reencrypt *params, ++ struct volume_key *vks, + struct luks2_reenc_context **rh) + { + int r; + struct luks2_reenc_context *tmp = NULL; + crypt_reencrypt_info ri = LUKS2_reenc_status(hdr); + ++ if (ri == CRYPT_REENCRYPT_NONE) { ++ log_err(cd, _("Device not marked for LUKS2 reencryption.")); ++ return -EINVAL; ++ } else if (ri == CRYPT_REENCRYPT_INVALID) ++ return -EINVAL; ++ ++ r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); ++ if (r < 0) ++ return r; ++ + if (ri == CRYPT_REENCRYPT_CLEAN) + r = reencrypt_load_clean(cd, hdr, device_size, &tmp, params); + else if (ri == CRYPT_REENCRYPT_CRASH) + r = reencrypt_load_crashed(cd, hdr, device_size, &tmp); +- else if (ri == CRYPT_REENCRYPT_NONE) { +- log_err(cd, _("Device not marked for LUKS2 reencryption.")); +- return -EINVAL; +- } else ++ else + r = -EINVAL; + + if (r < 0 || !tmp) { +@@ -2767,7 +2779,7 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, + rparams.device_size = required_size; + } + +- r = reencrypt_load(cd, hdr, device_size, &rparams, &rh); ++ r = reencrypt_load(cd, hdr, device_size, &rparams, *vks, &rh); + if (r < 0 || !rh) + goto err; + +@@ -2987,13 +2999,6 @@ static reenc_status_t reencrypt_step(struct crypt_device *cd, + { + int r; + +- /* update reencrypt keyslot protection parameters in memory only */ +- r = reenc_keyslot_update(cd, rh); +- if (r < 0) { +- log_dbg(cd, "Keyslot update failed."); +- return REENC_ERR; +- } +- + /* in memory only */ + r = reencrypt_make_segments(cd, hdr, rh, device_size); + if (r) +@@ -3258,6 +3263,15 @@ int crypt_reencrypt(struct crypt_device *cd, + + rs = REENC_OK; + ++ /* update reencrypt keyslot protection parameters in memory only */ ++ if (!quit && (rh->device_size > rh->progress)) { ++ r = reenc_keyslot_update(cd, rh); ++ if (r < 0) { ++ log_dbg(cd, "Keyslot update failed."); ++ return reencrypt_teardown(cd, hdr, rh, REENC_ERR, quit, progress); ++ } ++ } ++ + while (!quit && (rh->device_size > rh->progress)) { + rs = reencrypt_step(cd, hdr, rh, rh->device_size, rh->online); + if (rs != REENC_OK) +@@ -3290,7 +3304,7 @@ static int reencrypt_recovery(struct crypt_device *cd, + int r; + struct luks2_reenc_context *rh = NULL; + +- r = reencrypt_load(cd, hdr, device_size, NULL, &rh); ++ r = reencrypt_load(cd, hdr, device_size, NULL, vks, &rh); + if (r < 0) { + log_err(cd, _("Failed to load LUKS2 reencryption context.")); + return r; +diff --git a/lib/luks2/luks2_reencrypt_digest.c b/lib/luks2/luks2_reencrypt_digest.c +new file mode 100644 +index 0000000..7ee277c +--- /dev/null ++++ b/lib/luks2/luks2_reencrypt_digest.c +@@ -0,0 +1,381 @@ ++/* ++ * LUKS - Linux Unified Key Setup v2, reencryption digest helpers ++ * ++ * Copyright (C) 2022, Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2022, Ondrej Kozina ++ * Copyright (C) 2022, Milan Broz ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include "luks2_internal.h" ++#include ++ ++#define MAX_STR 64 ++ ++struct jtype { ++ enum { JNONE = 0, JSTR, JU64, JX64, JU32 } type; ++ json_object *jobj; ++ const char *id; ++}; ++ ++static size_t sr(struct jtype *j, uint8_t *ptr) ++{ ++ json_object *jobj; ++ size_t len = 0; ++ uint64_t u64; ++ uint32_t u32; ++ ++ if (!json_object_is_type(j->jobj, json_type_object)) ++ return 0; ++ ++ if (!json_object_object_get_ex(j->jobj, j->id, &jobj)) ++ return 0; ++ ++ switch(j->type) { ++ case JSTR: /* JSON string */ ++ if (!json_object_is_type(jobj, json_type_string)) ++ return 0; ++ len = strlen(json_object_get_string(jobj)); ++ if (len > MAX_STR) ++ return 0; ++ if (ptr) ++ memcpy(ptr, json_object_get_string(jobj), len); ++ break; ++ case JU64: /* Unsigned 64bit integer stored as string */ ++ if (!json_object_is_type(jobj, json_type_string)) ++ break; ++ len = sizeof(u64); ++ if (ptr) { ++ u64 = cpu_to_be64(crypt_jobj_get_uint64(jobj)); ++ memcpy(ptr, &u64, len); ++ } ++ break; ++ case JX64: /* Unsigned 64bit segment size (allows "dynamic") */ ++ if (!json_object_is_type(jobj, json_type_string)) ++ break; ++ if (!strcmp(json_object_get_string(jobj), "dynamic")) { ++ len = strlen("dynamic"); ++ if (ptr) ++ memcpy(ptr, json_object_get_string(jobj), len); ++ } else { ++ len = sizeof(u64); ++ u64 = cpu_to_be64(crypt_jobj_get_uint64(jobj)); ++ if (ptr) ++ memcpy(ptr, &u64, len); ++ } ++ break; ++ case JU32: /* Unsigned 32bit integer, stored as JSON int */ ++ if (!json_object_is_type(jobj, json_type_int)) ++ return 0; ++ len = sizeof(u32); ++ if (ptr) { ++ u32 = cpu_to_be32(crypt_jobj_get_uint32(jobj)); ++ memcpy(ptr, &u32, len); ++ } ++ break; ++ case JNONE: ++ return 0; ++ }; ++ ++ return len; ++} ++ ++static size_t srs(struct jtype j[], uint8_t *ptr) ++{ ++ size_t l, len = 0; ++ ++ while(j->jobj) { ++ l = sr(j, ptr); ++ if (!l) ++ return 0; ++ len += l; ++ if (ptr) ++ ptr += l; ++ j++; ++ } ++ return len; ++} ++ ++static size_t segment_linear_serialize(json_object *jobj_segment, uint8_t *buffer) ++{ ++ struct jtype j[] = { ++ { JSTR, jobj_segment, "type" }, ++ { JU64, jobj_segment, "offset" }, ++ { JX64, jobj_segment, "size" }, ++ {} ++ }; ++ return srs(j, buffer); ++} ++ ++static size_t segment_crypt_serialize(json_object *jobj_segment, uint8_t *buffer) ++{ ++ struct jtype j[] = { ++ { JSTR, jobj_segment, "type" }, ++ { JU64, jobj_segment, "offset" }, ++ { JX64, jobj_segment, "size" }, ++ { JU64, jobj_segment, "iv_tweak" }, ++ { JSTR, jobj_segment, "encryption" }, ++ { JU32, jobj_segment, "sector_size" }, ++ {} ++ }; ++ return srs(j, buffer); ++} ++ ++static size_t segment_serialize(json_object *jobj_segment, uint8_t *buffer) ++{ ++ json_object *jobj_type; ++ const char *segment_type; ++ ++ if (!json_object_object_get_ex(jobj_segment, "type", &jobj_type)) ++ return 0; ++ ++ if (!(segment_type = json_object_get_string(jobj_type))) ++ return 0; ++ ++ if (!strcmp(segment_type, "crypt")) ++ return segment_crypt_serialize(jobj_segment, buffer); ++ else if (!strcmp(segment_type, "linear")) ++ return segment_linear_serialize(jobj_segment, buffer); ++ ++ return 0; ++} ++ ++static size_t backup_segments_serialize(struct luks2_hdr *hdr, uint8_t *buffer) ++{ ++ json_object *jobj_segment; ++ size_t l, len = 0; ++ ++ jobj_segment = LUKS2_get_segment_by_flag(hdr, "backup-previous"); ++ if (!jobj_segment || !(l = segment_serialize(jobj_segment, buffer))) ++ return 0; ++ len += l; ++ if (buffer) ++ buffer += l; ++ ++ jobj_segment = LUKS2_get_segment_by_flag(hdr, "backup-final"); ++ if (!jobj_segment || !(l = segment_serialize(jobj_segment, buffer))) ++ return 0; ++ len += l; ++ if (buffer) ++ buffer += l; ++ ++ jobj_segment = LUKS2_get_segment_by_flag(hdr, "backup-moved-segment"); ++ if (jobj_segment) { ++ if (!(l = segment_serialize(jobj_segment, buffer))) ++ return 0; ++ len += l; ++ } ++ ++ return len; ++} ++ ++static size_t reenc_keyslot_serialize(struct luks2_hdr *hdr, uint8_t *buffer) ++{ ++ json_object *jobj_keyslot, *jobj_area, *jobj_type; ++ const char *area_type; ++ int keyslot_reencrypt; ++ ++ keyslot_reencrypt = LUKS2_find_keyslot(hdr, "reencrypt"); ++ if (keyslot_reencrypt < 0) ++ return 0; ++ ++ if (!(jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot_reencrypt))) ++ return 0; ++ ++ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) ++ return 0; ++ ++ if (!json_object_object_get_ex(jobj_area, "type", &jobj_type)) ++ return 0; ++ ++ if (!(area_type = json_object_get_string(jobj_type))) ++ return 0; ++ ++ struct jtype j[] = { ++ { JSTR, jobj_keyslot, "mode" }, ++ { JSTR, jobj_keyslot, "direction" }, ++ { JSTR, jobj_area, "type" }, ++ { JU64, jobj_area, "offset" }, ++ { JU64, jobj_area, "size" }, ++ {} ++ }; ++ struct jtype j_datashift[] = { ++ { JSTR, jobj_keyslot, "mode" }, ++ { JSTR, jobj_keyslot, "direction" }, ++ { JSTR, jobj_area, "type" }, ++ { JU64, jobj_area, "offset" }, ++ { JU64, jobj_area, "size" }, ++ { JU64, jobj_area, "shift_size" }, ++ {} ++ }; ++ struct jtype j_checksum[] = { ++ { JSTR, jobj_keyslot, "mode" }, ++ { JSTR, jobj_keyslot, "direction" }, ++ { JSTR, jobj_area, "type" }, ++ { JU64, jobj_area, "offset" }, ++ { JU64, jobj_area, "size" }, ++ { JSTR, jobj_area, "hash" }, ++ { JU32, jobj_area, "sector_size" }, ++ {} ++ }; ++ ++ if (!strcmp(area_type, "datashift")) ++ return srs(j_datashift, buffer); ++ else if (!strcmp(area_type, "checksum")) ++ return srs(j_checksum, buffer); ++ ++ return srs(j, buffer); ++} ++ ++static size_t blob_serialize(void *blob, size_t length, uint8_t *buffer) ++{ ++ if (buffer) ++ memcpy(buffer, blob, length); ++ ++ return length; ++} ++ ++static int reencrypt_assembly_verification_data(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks, ++ struct volume_key **verification_data) ++{ ++ uint8_t *ptr; ++ int digest_new, digest_old; ++ struct volume_key *data = NULL, *vk_old = NULL, *vk_new = NULL; ++ size_t keyslot_data_len, segments_data_len, data_len = 2; ++ ++ /* Keys - calculate length */ ++ digest_new = LUKS2_reencrypt_digest_new(hdr); ++ digest_old = LUKS2_reencrypt_digest_old(hdr); ++ ++ if (digest_old >= 0) { ++ vk_old = crypt_volume_key_by_id(vks, digest_old); ++ if (!vk_old) ++ return -EINVAL; ++ data_len += blob_serialize(vk_old->key, vk_old->keylength, NULL); ++ } ++ ++ if (digest_new >= 0 && digest_old != digest_new) { ++ vk_new = crypt_volume_key_by_id(vks, digest_new); ++ if (!vk_new) ++ return -EINVAL; ++ data_len += blob_serialize(vk_new->key, vk_new->keylength, NULL); ++ } ++ ++ if (data_len == 2) ++ return -EINVAL; ++ ++ /* Metadata - calculate length */ ++ if (!(keyslot_data_len = reenc_keyslot_serialize(hdr, NULL))) ++ return -EINVAL; ++ data_len += keyslot_data_len; ++ ++ if (!(segments_data_len = backup_segments_serialize(hdr, NULL))) ++ return -EINVAL; ++ data_len += segments_data_len; ++ ++ /* Alloc and fill serialization data */ ++ data = crypt_alloc_volume_key(data_len, NULL); ++ if (!data) ++ return -ENOMEM; ++ ++ ptr = (uint8_t*)data->key; ++ ++ /* v2 */ ++ *ptr++ = 0x76; ++ *ptr++ = 0x32; ++ ++ if (vk_old) ++ ptr += blob_serialize(vk_old->key, vk_old->keylength, ptr); ++ ++ if (vk_new) ++ ptr += blob_serialize(vk_new->key, vk_new->keylength, ptr); ++ ++ if (!reenc_keyslot_serialize(hdr, ptr)) ++ goto bad; ++ ptr += keyslot_data_len; ++ ++ if (!backup_segments_serialize(hdr, ptr)) ++ goto bad; ++ ptr += segments_data_len; ++ ++ assert((size_t)(ptr - (uint8_t*)data->key) == data_len); ++ ++ *verification_data = data; ++ ++ return 0; ++bad: ++ crypt_free_volume_key(data); ++ return -EINVAL; ++} ++ ++int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks) ++{ ++ int digest_reencrypt, keyslot_reencrypt, r; ++ struct volume_key *data; ++ ++ keyslot_reencrypt = LUKS2_find_keyslot(hdr, "reencrypt"); ++ if (keyslot_reencrypt < 0) ++ return keyslot_reencrypt; ++ ++ r = reencrypt_assembly_verification_data(cd, hdr, vks, &data); ++ if (r < 0) ++ return r; ++ ++ r = LUKS2_digest_create(cd, "pbkdf2", hdr, data); ++ crypt_free_volume_key(data); ++ if (r < 0) ++ return r; ++ ++ digest_reencrypt = r; ++ ++ r = LUKS2_digest_assign(cd, hdr, keyslot_reencrypt, CRYPT_ANY_DIGEST, 0, 0); ++ if (r < 0) ++ return r; ++ ++ return LUKS2_digest_assign(cd, hdr, keyslot_reencrypt, digest_reencrypt, 1, 0); ++} ++ ++int LUKS2_reencrypt_digest_verify(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks) ++{ ++ int r, keyslot_reencrypt; ++ struct volume_key *data; ++ ++ keyslot_reencrypt = LUKS2_find_keyslot(hdr, "reencrypt"); ++ if (keyslot_reencrypt < 0) ++ return keyslot_reencrypt; ++ ++ r = reencrypt_assembly_verification_data(cd, hdr, vks, &data); ++ if (r < 0) ++ return r; ++ ++ r = LUKS2_digest_verify(cd, hdr, data, keyslot_reencrypt); ++ crypt_free_volume_key(data); ++ ++ if (r < 0) { ++ if (r == -ENOENT) ++ log_dbg(cd, "Reencryption digest is missing."); ++ log_err(cd, _("Reencryption metadata is invalid.")); ++ } else ++ log_dbg(cd, "Reencryption metadata verified."); ++ ++ return r; ++} +diff --git a/lib/setup.c b/lib/setup.c +index 567f262..21a41a3 100644 +--- a/lib/setup.c ++++ b/lib/setup.c +@@ -4023,6 +4023,12 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd, + keyslot = r; + } + ++ if (r >= 0) { ++ r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); ++ if (r < 0) ++ goto err; ++ } ++ + log_dbg(cd, "Entering clean reencryption state mode."); + + if (r >= 0) +@@ -4050,8 +4056,9 @@ static int _open_and_activate_luks2(struct crypt_device *cd, + uint32_t flags) + { + crypt_reencrypt_info ri; +- int r; ++ int r, rv; + struct luks2_hdr *hdr = &cd->u.luks2.hdr; ++ struct volume_key *vks = NULL; + + ri = LUKS2_reenc_status(hdr); + if (ri == CRYPT_REENCRYPT_INVALID) +@@ -4061,9 +4068,17 @@ static int _open_and_activate_luks2(struct crypt_device *cd, + if (name) + r = _open_and_activate_reencrypt_device(cd, hdr, keyslot, name, passphrase, + passphrase_size, flags); +- else ++ else { + r = _open_all_keys(cd, hdr, keyslot, passphrase, +- passphrase_size, flags, NULL); ++ passphrase_size, flags, &vks); ++ if (r < 0) ++ return r; ++ ++ rv = LUKS2_reencrypt_digest_verify(cd, hdr, vks); ++ crypt_free_volume_key(vks); ++ if (rv < 0) ++ return rv; ++ } + } else + r = _open_and_activate(cd, keyslot, name, passphrase, + passphrase_size, flags); +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 8c1f2b7..9d4958b 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -37,6 +37,7 @@ lib/luks2/luks2_keyslot_luks2.c + lib/luks2/luks2_keyslot_reenc.c + lib/luks2/luks2_luks1_convert.c + lib/luks2/luks2_reencrypt.c ++lib/luks2/luks2_reencrypt_digest.c + lib/luks2/luks2_segment.c + lib/luks2/luks2_token.c + lib/luks2/luks2_token_keyring.c +-- +2.23.0 + diff --git a/cryptsetup.spec b/cryptsetup.spec index 295c0b2f9db5a4ce4348f5070be9764aa39b0a75..3ff2f24ec45d873f02177dcc3daa5c4af906a29b 100644 --- a/cryptsetup.spec +++ b/cryptsetup.spec @@ -1,6 +1,6 @@ Name: cryptsetup Version: 2.3.3 -Release: 5 +Release: 6 Summary: Utility used to conveniently set up disk encryption License: GPLv2+ and LGPLv2+ URL: https://gitlab.com/cryptsetup/cryptsetup @@ -11,6 +11,7 @@ Patch2: 0002-fix-compat-test.patch Patch3: 0003-Check-segment-gaps-regardless-of-heap-space.patch Patch4: 0004-Fix-posible-buffer-overflows-in-LUKS-conversion.patch Patch5: 0005-Fix-a-memleak-in-blockwise-test.patch +Patch6: 0006-CVE-2021-4122.patch BuildRequires: openssl-devel, popt-devel, device-mapper-devel, gcc BuildRequires: libuuid-devel, json-c-devel, libargon2-devel, libpwquality-devel, libblkid-devel @@ -113,6 +114,9 @@ make check %{_mandir}/man8/* %changelog +* Mon Jan 24 2022 yanglongakang - 2.3.3-6 +- Fix CVE-2021-4122 + * Fri Jul 30 2021 chenyanpanHW - 2.3.3-5 - DESC: delete -S git from %autosetup, and delete BuildRequires git