Blame SOURCES/cryptsetup-2.4.2-Fix-bogus-memory-allocation-if-LUKS2-header-size-is-.patch

a743d1
From 9576549fee9228cabd9ceee27739a30caab5a7f6 Mon Sep 17 00:00:00 2001
a743d1
From: Milan Broz <gmazyland@gmail.com>
a743d1
Date: Tue, 9 Nov 2021 11:54:27 +0100
a743d1
Subject: [PATCH] Fix bogus memory allocation if LUKS2 header size is invalid.
a743d1
a743d1
LUKS2 code read the whole header to buffer to verify checksum,
a743d1
so malloc is called on unvalidated input size parameter.
a743d1
a743d1
This can cause out of memory or unintentional device reads.
a743d1
(Header validation will fail later anyway - the size is unsupported.)
a743d1
a743d1
Just do not allow too small and too big allocations here and fail quickly.
a743d1
a743d1
Fixes: #683.
a743d1
---
a743d1
 lib/luks2/luks2_disk_metadata.c               | 20 +++-
a743d1
 ...ks2-metadata-size-invalid-secondary.img.sh | 96 +++++++++++++++++++
a743d1
 ...enerate-luks2-metadata-size-invalid.img.sh | 94 ++++++++++++++++++
a743d1
 tests/luks2-validation-test                   |  2 +
a743d1
 4 files changed, 208 insertions(+), 4 deletions(-)
a743d1
 create mode 100755 tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh
a743d1
 create mode 100755 tests/generators/generate-luks2-metadata-size-invalid.img.sh
a743d1
a743d1
diff --git a/lib/luks2/luks2_disk_metadata.c b/lib/luks2/luks2_disk_metadata.c
a743d1
index 502b0226..0500d5c7 100644
a743d1
--- a/lib/luks2/luks2_disk_metadata.c
a743d1
+++ b/lib/luks2/luks2_disk_metadata.c
a743d1
@@ -195,6 +195,8 @@ static int hdr_disk_sanity_check_pre(struct crypt_device *cd,
a743d1
 				     size_t *hdr_json_size, int secondary,
a743d1
 				     uint64_t offset)
a743d1
 {
a743d1
+	uint64_t hdr_size;
a743d1
+
a743d1
 	if (memcmp(hdr->magic, secondary ? LUKS2_MAGIC_2ND : LUKS2_MAGIC_1ST, LUKS2_MAGIC_L))
a743d1
 		return -EINVAL;
a743d1
 
a743d1
@@ -209,19 +211,26 @@ static int hdr_disk_sanity_check_pre(struct crypt_device *cd,
a743d1
 		return -EINVAL;
a743d1
 	}
a743d1
 
a743d1
-	if (secondary && (offset != be64_to_cpu(hdr->hdr_size))) {
a743d1
+	hdr_size = be64_to_cpu(hdr->hdr_size);
a743d1
+
a743d1
+	if (hdr_size < LUKS2_HDR_16K_LEN || hdr_size > LUKS2_HDR_OFFSET_MAX) {
a743d1
+		log_dbg(cd, "LUKS2 header has bogus size 0x%04x.", (unsigned)hdr_size);
a743d1
+		return -EINVAL;
a743d1
+	}
a743d1
+
a743d1
+	if (secondary && (offset != hdr_size)) {
a743d1
 		log_dbg(cd, "LUKS2 offset 0x%04x in secondary header does not match size 0x%04x.",
a743d1
-			(unsigned)offset, (unsigned)be64_to_cpu(hdr->hdr_size));
a743d1
+			(unsigned)offset, (unsigned)hdr_size);
a743d1
 		return -EINVAL;
a743d1
 	}
a743d1
 
a743d1
 	/* FIXME: sanity check checksum alg. */
a743d1
 
a743d1
 	log_dbg(cd, "LUKS2 header version %u of size %u bytes, checksum %s.",
a743d1
-		(unsigned)be16_to_cpu(hdr->version), (unsigned)be64_to_cpu(hdr->hdr_size),
a743d1
+		(unsigned)be16_to_cpu(hdr->version), (unsigned)hdr_size,
a743d1
 		hdr->checksum_alg);
a743d1
 
a743d1
-	*hdr_json_size = be64_to_cpu(hdr->hdr_size) - LUKS2_HDR_BIN_LEN;
a743d1
+	*hdr_json_size = hdr_size - LUKS2_HDR_BIN_LEN;
a743d1
 	return 0;
a743d1
 }
a743d1
 
a743d1
@@ -252,6 +261,9 @@ static int hdr_read_disk(struct crypt_device *cd,
a743d1
 		return -EIO;
a743d1
 	}
a743d1
 
a743d1
+	/*
a743d1
+	 * hdr_json_size is validated if this call succeeds
a743d1
+	 */
a743d1
 	r = hdr_disk_sanity_check_pre(cd, hdr_disk, &hdr_json_size, secondary, offset);
a743d1
 	if (r < 0) {
a743d1
 		return r;
a743d1
diff --git a/tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh
a743d1
new file mode 100755
a743d1
index 00000000..4dd484e9
a743d1
--- /dev/null
a743d1
+++ b/tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh
a743d1
@@ -0,0 +1,96 @@
a743d1
+#!/bin/bash
a743d1
+
a743d1
+. lib.sh
a743d1
+
a743d1
+#
a743d1
+# *** Description ***
a743d1
+#
a743d1
+# generate primary with predefined json_size. There's only limited
a743d1
+# set of values allowed as json size in config section of LUKS2
a743d1
+# metadata
a743d1
+#
a743d1
+# secondary header is corrupted on purpose as well
a743d1
+#
a743d1
+
a743d1
+# $1 full target dir
a743d1
+# $2 full source luks2 image
a743d1
+
a743d1
+function prepare()
a743d1
+{
a743d1
+	cp $SRC_IMG $TGT_IMG
a743d1
+	test -d $TMPDIR || mkdir $TMPDIR
a743d1
+	read_luks2_json0 $TGT_IMG $TMPDIR/json0
a743d1
+	read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
a743d1
+	read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
a743d1
+}
a743d1
+
a743d1
+function generate()
a743d1
+{
a743d1
+	TEST_MDA_SIZE=$LUKS2_HDR_SIZE_1M
a743d1
+
a743d1
+	TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
a743d1
+	TEST_MDA_SIZE_BOGUS_BYTES=$((TEST_MDA_SIZE*512*2*1024))
a743d1
+	TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
a743d1
+	KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
a743d1
+	JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
a743d1
+	JSON_SIZE=$((TEST_JSN_SIZE*512))
a743d1
+	DATA_OFFSET=16777216
a743d1
+
a743d1
+	json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
a743d1
+		   '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
a743d1
+		    .config.json_size = $jsize |
a743d1
+		    .segments."0".offset = $off' $TMPDIR/json0)
a743d1
+	test -n "$json_str" || exit 2
a743d1
+	test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
a743d1
+
a743d1
+	write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
a743d1
+
a743d1
+	write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
a743d1
+	write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BOGUS_BYTES
a743d1
+
a743d1
+	write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
a743d1
+
a743d1
+	merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
a743d1
+	merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
a743d1
+
a743d1
+	erase_checksum $TMPDIR/area0
a743d1
+	chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
a743d1
+	write_checksum $chks0 $TMPDIR/area0
a743d1
+
a743d1
+	erase_checksum $TMPDIR/area1
a743d1
+	chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
a743d1
+	write_checksum $chks0 $TMPDIR/area1
a743d1
+
a743d1
+	kill_bin_hdr $TMPDIR/area0
a743d1
+
a743d1
+	write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
a743d1
+	write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
a743d1
+}
a743d1
+
a743d1
+function check()
a743d1
+{
a743d1
+	read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $TEST_MDA_SIZE
a743d1
+	local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
a743d1
+	test "$str_res0" = "VACUUM" || exit 2
a743d1
+	read_luks2_json1 $TGT_IMG $TMPDIR/json_res1 $TEST_JSN_SIZE
a743d1
+	jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
a743d1
+		'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
a743d1
+		    (.config.json_size != $jsize)
a743d1
+		then error("Unexpected value in result json") else empty end' $TMPDIR/json_res1 || exit 5
a743d1
+}
a743d1
+
a743d1
+function cleanup()
a743d1
+{
a743d1
+	rm -f $TMPDIR/*
a743d1
+	rm -fd $TMPDIR
a743d1
+}
a743d1
+
a743d1
+test $# -eq 2 || exit 1
a743d1
+
a743d1
+TGT_IMG=$1/$(test_img_name $0)
a743d1
+SRC_IMG=$2
a743d1
+
a743d1
+prepare
a743d1
+generate
a743d1
+check
a743d1
+cleanup
a743d1
diff --git a/tests/generators/generate-luks2-metadata-size-invalid.img.sh b/tests/generators/generate-luks2-metadata-size-invalid.img.sh
a743d1
new file mode 100755
a743d1
index 00000000..6b9c0cf7
a743d1
--- /dev/null
a743d1
+++ b/tests/generators/generate-luks2-metadata-size-invalid.img.sh
a743d1
@@ -0,0 +1,94 @@
a743d1
+#!/bin/bash
a743d1
+
a743d1
+. lib.sh
a743d1
+
a743d1
+#
a743d1
+# *** Description ***
a743d1
+#
a743d1
+# generate primary with predefined json_size. There's only limited
a743d1
+# set of values allowed as json size in config section of LUKS2
a743d1
+# metadata
a743d1
+#
a743d1
+# secondary header is corrupted on purpose as well
a743d1
+#
a743d1
+
a743d1
+# $1 full target dir
a743d1
+# $2 full source luks2 image
a743d1
+
a743d1
+function prepare()
a743d1
+{
a743d1
+	cp $SRC_IMG $TGT_IMG
a743d1
+	test -d $TMPDIR || mkdir $TMPDIR
a743d1
+	read_luks2_json0 $TGT_IMG $TMPDIR/json0
a743d1
+	read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
a743d1
+	read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
a743d1
+}
a743d1
+
a743d1
+function generate()
a743d1
+{
a743d1
+	TEST_MDA_SIZE=$LUKS2_HDR_SIZE_1M
a743d1
+
a743d1
+	TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
a743d1
+	TEST_MDA_SIZE_BOGUS_BYTES=$((TEST_MDA_SIZE*512*2*1024))
a743d1
+	TEST_JSN_SIZE=$((TEST_MDA_SIZE-LUKS2_BIN_HDR_SIZE))
a743d1
+	KEYSLOTS_OFFSET=$((TEST_MDA_SIZE*1024))
a743d1
+	JSON_DIFF=$(((TEST_MDA_SIZE-LUKS2_HDR_SIZE)*1024))
a743d1
+	JSON_SIZE=$((TEST_JSN_SIZE*512))
a743d1
+	DATA_OFFSET=16777216
a743d1
+
a743d1
+	json_str=$(jq -c --arg jdiff $JSON_DIFF --arg jsize $JSON_SIZE --arg off $DATA_OFFSET \
a743d1
+		   '.keyslots[].area.offset |= ( . | tonumber + ($jdiff | tonumber) | tostring) |
a743d1
+		    .config.json_size = $jsize |
a743d1
+		    .segments."0".offset = $off' $TMPDIR/json0)
a743d1
+	test -n "$json_str" || exit 2
a743d1
+	test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
a743d1
+
a743d1
+	write_luks2_json "$json_str" $TMPDIR/json0 $TEST_JSN_SIZE
a743d1
+
a743d1
+	write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BOGUS_BYTES
a743d1
+	write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BOGUS_BYTES
a743d1
+
a743d1
+	merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $TEST_JSN_SIZE
a743d1
+	merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json0 $TMPDIR/area1 $TEST_JSN_SIZE
a743d1
+
a743d1
+	erase_checksum $TMPDIR/area0
a743d1
+	chks0=$(calc_sha256_checksum_file $TMPDIR/area0)
a743d1
+	write_checksum $chks0 $TMPDIR/area0
a743d1
+
a743d1
+	erase_checksum $TMPDIR/area1
a743d1
+	chks0=$(calc_sha256_checksum_file $TMPDIR/area1)
a743d1
+	write_checksum $chks0 $TMPDIR/area1
a743d1
+
a743d1
+	kill_bin_hdr $TMPDIR/area1
a743d1
+
a743d1
+	write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $TEST_MDA_SIZE
a743d1
+	write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $TEST_MDA_SIZE
a743d1
+}
a743d1
+
a743d1
+function check()
a743d1
+{
a743d1
+	read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $TEST_MDA_SIZE
a743d1
+	local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
a743d1
+	test "$str_res1" = "VACUUM" || exit 2
a743d1
+	read_luks2_json0 $TGT_IMG $TMPDIR/json_res0 $TEST_JSN_SIZE
a743d1
+	jq -c --arg koff $KEYSLOTS_OFFSET --arg jsize $JSON_SIZE \
a743d1
+		'if ([.keyslots[].area.offset] | map(tonumber) | min | tostring != $koff) or
a743d1
+		    (.config.json_size != $jsize)
a743d1
+		then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
a743d1
+}
a743d1
+
a743d1
+function cleanup()
a743d1
+{
a743d1
+	rm -f $TMPDIR/*
a743d1
+	rm -fd $TMPDIR
a743d1
+}
a743d1
+
a743d1
+test $# -eq 2 || exit 1
a743d1
+
a743d1
+TGT_IMG=$1/$(test_img_name $0)
a743d1
+SRC_IMG=$2
a743d1
+
a743d1
+prepare
a743d1
+generate
a743d1
+check
a743d1
+cleanup
a743d1
diff --git a/tests/luks2-validation-test b/tests/luks2-validation-test
a743d1
index 04183fbc..f771e1f9 100755
a743d1
--- a/tests/luks2-validation-test
a743d1
+++ b/tests/luks2-validation-test
a743d1
@@ -229,6 +229,8 @@ RUN luks2-metadata-size-512k-secondary.img		"R" "Valid 512KiB metadata size in s
a743d1
 RUN luks2-metadata-size-1m-secondary.img		"R" "Valid 1MiB metadata size in secondary hdr failed to validate"
a743d1
 RUN luks2-metadata-size-2m-secondary.img		"R" "Valid 2MiB metadata size in secondary hdr failed to validate"
a743d1
 RUN luks2-metadata-size-4m-secondary.img		"R" "Valid 4MiB metadata size in secondary hdr failed to validate"
a743d1
+RUN luks2-metadata-size-invalid.img			"F" "Invalid metadata size in secondary hdr not rejected"
a743d1
+RUN luks2-metadata-size-invalid-secondary.img		"F" "Invalid metadata size in secondary hdr not rejected"
a743d1
 
a743d1
 remove_mapping
a743d1
 
a743d1
-- 
a743d1
2.27.0
a743d1