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

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