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

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