diff mbox series

[v5,3/4] ima_tpm.sh: Fix calculating boot aggregate

Message ID 20201214221946.6340-4-pvorel@suse.cz (mailing list archive)
State New, archived
Headers show
Series TPM 2.0 fixes in IMA tests | expand

Commit Message

Petr Vorel Dec. 14, 2020, 10:19 p.m. UTC
for TPM 2.0 or kernel >= v5.8-rc1:
6f1a1d103b48 ima: ("Switch to ima_hash_algo for boot aggregate")

Test still fails with newer TPM 2.0 on kernel < v5.8-rc1.

Test was failing, because it expect SHA1 (we ignore MD5) hash, but for TPM 2.0
is now used IMA default hash algorithm (by default default SHA256).
This is similar for entries in IMA measurement list so we can reuse
already existing code.

Reading other algorithms than SHA1 or support TPM 2.0 requires evmctl
>= 1.3.1 (1.3 would also work for test1, but will be required for test2).

Although recent evmctl is recommended, to support older kernels and TPMs
which support only SHA1, get boot aggregate with old our legacy
ima_boot_aggregate.c.

Also fixed cases:
* testing with no TPM device:
* TPM TPM 2.0 devices which does not export event log
(/sys/kernel/security/tpm0/binary_bios_measurements).

Also fixed test without TPM device (when IMA TPM-bypass is tested)
as some TPM 2.0 devices does not export event log
(/sys/kernel/security/tpm0/binary_bios_measurements).
This does not require evmctl at all.

Also try best to detect TPM major version (1, 2 or none - assume
TPM-bypass). This fixes testing with TPM 2.0 device which does not
export event log (/sys/kernel/security/tpm0/binary_bios_measurements):
not wrongly assuming TPM-bypass when kernel didn't export other TPM
2.0 files we check in get_tpm_version() but bios boot aggregate is
correct (i.e. not 0x00s). In that case evmctl ima_boot_aggregate can get
boot aggregate even without TPM event log.

Co-developed-by: Mimi Zohar <zohar@linux.ibm.com>
Signed-off-by: Petr Vorel <pvorel@suse.cz>
---
Changes v4->v5:
* add .needs_root = 1

 .../security/integrity/ima/tests/ima_tpm.sh   | 176 +++++++++++++++---
 1 file changed, 154 insertions(+), 22 deletions(-)

Comments

Mimi Zohar Dec. 17, 2020, 6:12 p.m. UTC | #1
Hi Petr,

On Mon, 2020-12-14 at 23:19 +0100, Petr Vorel wrote:
> for TPM 2.0 or kernel >= v5.8-rc1:
> 6f1a1d103b48 ima: ("Switch to ima_hash_algo for boot aggregate")
> 
> Test still fails with newer TPM 2.0 on kernel < v5.8-rc1.

The above commit was backported in stable.  Do you know if the failing
systems backported the above patch?   I've recently asked for commit
20c59ce010f8 ("ima: extend boot_aggregate with kernel measurements")
also be backported.

> 
> Test was failing, because it expect SHA1 (we ignore MD5) hash, but for TPM 2.0
> is now used IMA default hash algorithm (by default default SHA256).
> This is similar for entries in IMA measurement list so we can reuse
> already existing code.
> 
> Reading other algorithms than SHA1 or support TPM 2.0 requires evmctl
> >= 1.3.1 (1.3 would also work for test1, but will be required for test2).
> 
> Although recent evmctl is recommended, to support older kernels and TPMs
> which support only SHA1, get boot aggregate with old our legacy
> ima_boot_aggregate.c.

^ the LTP legacy ima_boot_aggregate.c still works, without the evmctl
dependency.

> 
> Also fixed cases:
> * testing with no TPM device:
> * TPM TPM 2.0 devices which does not export event log
> (/sys/kernel/security/tpm0/binary_bios_measurements).

^ firmware which does not export the TPM 2.0 binary event log

> 
> Also fixed test without TPM device (when IMA TPM-bypass is tested)
> as some TPM 2.0 devices does not export event log
> (/sys/kernel/security/tpm0/binary_bios_measurements).

This looks like a duplicate of above.  Maybe just add another bullet
*
detecting IMA TPM-bypass mode

> This does not require evmctl at all.

I assume this comment refers to TPM 2.0 calculating the
"boot_aggregate" based on the existing PCR values, as opposed to TPM
1.2 which first walks the TPM event log, calculating the PCRs.

> 
> Also try best to detect TPM major version (1, 2 or none - assume
> TPM-bypass). This fixes testing with TPM 2.0 device which does not
> export event log (/sys/kernel/security/tpm0/binary_bios_measurements):
> not wrongly assuming TPM-bypass when kernel didn't export other TPM
> 2.0 files we check in get_tpm_version() but bios boot aggregate is
> correct (i.e. not 0x00s). In that case evmctl ima_boot_aggregate can get
> boot aggregate even without TPM event log.
> 
> Co-developed-by: Mimi Zohar <zohar@linux.ibm.com>
> Signed-off-by: Petr Vorel <pvorel@suse.cz>

Thanks, Petr!

Mimi
Petr Vorel Dec. 17, 2020, 7:36 p.m. UTC | #2
Hi Mimi,

TL;DR: thank you for comments, I update commit message.
Details below.

> Hi Petr,

> On Mon, 2020-12-14 at 23:19 +0100, Petr Vorel wrote:
> > for TPM 2.0 or kernel >= v5.8-rc1:
> > 6f1a1d103b48 ima: ("Switch to ima_hash_algo for boot aggregate")

> > Test still fails with newer TPM 2.0 on kernel < v5.8-rc1.

> The above commit was backported in stable.  Do you know if the failing
> systems backported the above patch?
SLES kernel got it.

> I've recently asked for commit
> 20c59ce010f8 ("ima: extend boot_aggregate with kernel measurements")
> also be backported.
I see, it got backported to v5.4 (LTS).

FYI shell API doesn't have yet support for hint for commits to backport
(my TODO: #700 [1]). Once it's implemented, I'll add 20c59ce010f8 as this tag.

[1] https://github.com/linux-test-project/ltp/issues/700

> > Test was failing, because it expect SHA1 (we ignore MD5) hash, but for TPM 2.0
> > is now used IMA default hash algorithm (by default default SHA256).
> > This is similar for entries in IMA measurement list so we can reuse
> > already existing code.

> > Reading other algorithms than SHA1 or support TPM 2.0 requires evmctl
> > >= 1.3.1 (1.3 would also work for test1, but will be required for test2).

> > Although recent evmctl is recommended, to support older kernels and TPMs
> > which support only SHA1, get boot aggregate with old our legacy
> > ima_boot_aggregate.c.

> ^ the LTP legacy ima_boot_aggregate.c still works, without the evmctl
> dependency.
Yes, I meant in my description this LTP legacy ima_boot_aggregate.c.
Test does not require evmctl for SHA1 hash.

So maybe:
Although recent evmctl is recommended, for older kernels and TPMs
which support only SHA1 is still used the legacy ima_boot_aggregate.c
(no evmctl update required).

> > Also fixed cases:
> > * testing with no TPM device:
> > * TPM TPM 2.0 devices which does not export event log
> > (/sys/kernel/security/tpm0/binary_bios_measurements).

> ^ firmware which does not export the TPM 2.0 binary event log
+1

> > Also fixed test without TPM device (when IMA TPM-bypass is tested)
> > as some TPM 2.0 devices does not export event log
> > (/sys/kernel/security/tpm0/binary_bios_measurements).

> This looks like a duplicate of above.  Maybe just add another bullet
> *
> detecting IMA TPM-bypass mode
+1

> > This does not require evmctl at all.

> I assume this comment refers to TPM 2.0 calculating the
> "boot_aggregate" based on the existing PCR values, as opposed to TPM
> 1.2 which first walks the TPM event log, calculating the PCRs.
No, that's meant for IMA TPM-bypass (test1). Because we only check zero.
Both TPM 1.2 and TPM 2.0 require evmctl for reading PCR-10 (test2).
So just a note for * detecting IMA TPM-bypass mode.

> > Also try best to detect TPM major version (1, 2 or none - assume
> > TPM-bypass). This fixes testing with TPM 2.0 device which does not
> > export event log (/sys/kernel/security/tpm0/binary_bios_measurements):
> > not wrongly assuming TPM-bypass when kernel didn't export other TPM
> > 2.0 files we check in get_tpm_version() but bios boot aggregate is
> > correct (i.e. not 0x00s). In that case evmctl ima_boot_aggregate can get
> > boot aggregate even without TPM event log.

> > Co-developed-by: Mimi Zohar <zohar@linux.ibm.com>
> > Signed-off-by: Petr Vorel <pvorel@suse.cz>

> Thanks, Petr!
Thanks a lot for patient review and info.

Kind regards,
Petr

> Mimi
diff mbox series

Patch

diff --git a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
index dc958eb5c..195fcb16c 100755
--- a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
+++ b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
@@ -7,39 +7,171 @@ 
 # Verify the boot and PCR aggregates.
 
 TST_CNT=2
-TST_NEEDS_CMDS="awk cut ima_boot_aggregate"
+TST_NEEDS_CMDS="awk cut"
+TST_SETUP="setup"
 
 . ima_setup.sh
 
-test1()
-{
-	tst_res TINFO "verify boot aggregate"
+EVMCTL_REQUIRED='1.3.1'
+ERRMSG_EVMCTL="=> install evmctl >= $EVMCTL_REQUIRED"
 
-	local zero="0000000000000000000000000000000000000000"
-	local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements"
-	local ima_measurements="$ASCII_MEASUREMENTS"
-	local boot_aggregate boot_hash line
+setup()
+{
+	local config="${KCONFIG_PATH:-/boot/config-$(uname -r)}"
+	local line tmp
 
-	# IMA boot aggregate
-	read line < $ima_measurements
-	boot_hash=$(echo $line | awk '{print $(NF-1)}' | cut -d':' -f2)
+	read line < $ASCII_MEASUREMENTS
+	if tmp=$(get_algorithm_digest "$line"); then
+		ALGORITHM=$(echo "$tmp" | cut -d'|' -f1)
+		DIGEST=$(echo "$tmp" | cut -d'|' -f2)
+	else
+		tst_brk TBROK "failed to get algorithm/digest: $tmp"
+	fi
+	tst_res TINFO "used algorithm: $ALGORITHM"
 
-	if [ ! -f "$tpm_bios" ]; then
-		tst_res TINFO "TPM Hardware Support not enabled in kernel or no TPM chip found"
+	TPM_VERSION="$(get_tpm_version)"
+	if [ -z "$TPM_VERSION" ]; then
+		tst_res TINFO "TPM hardware support not enabled in kernel or no TPM chip found, testing TPM-bypass"
+	else
+		tst_res TINFO "TMP major version: $TPM_VERSION"
+	fi
 
-		if [ "$boot_hash" = "$zero" ]; then
-			tst_res TPASS "bios boot aggregate is 0"
-		else
-			tst_res TFAIL "bios boot aggregate is not 0"
+	if ! check_evmctl $EVMCTL_REQUIRED; then
+		if [ "$ALGORITHM" != "sha1" ]; then
+			tst_brk TCONF "algorithm not sha1 ($ALGORITHM) $ERRMSG_EVMCTL"
 		fi
+		MISSING_EVMCTL=1
+	fi
+
+	if [ -r "$config" ]; then
+		tst_res TINFO "TPM kernel config:"
+		for i in $(grep -e ^CONFIG_.*_TPM -e ^CONFIG_TCG $config); do
+			tst_res TINFO "$i"
+		done
+	fi
+}
+
+# check_evmctl REQUIRED_TPM_VERSION
+# return: 0: evmctl is new enough, 1: version older than required (or version < v0.9)
+check_evmctl()
+{
+	local required="$1"
+
+	local r1="$(echo $required | cut -d. -f1)"
+	local r2="$(echo $required | cut -d. -f2)"
+	local r3="$(echo $required | cut -d. -f3)"
+	[ -z "$r3" ] && r3=0
+
+	tst_is_int "$r1" || tst_brk TBROK "required major version not int ($v1)"
+	tst_is_int "$r2" || tst_brk TBROK "required minor version not int ($v2)"
+	tst_is_int "$r3" || tst_brk TBROK "required patch version not int ($v3)"
+
+	tst_check_cmds evmctl || return 1
+
+	local v="$(evmctl --version | cut -d' ' -f2)"
+	[ -z "$v" ] && return 1
+	tst_res TINFO "evmctl version: $v"
+
+	local v1="$(echo $v | cut -d. -f1)"
+	local v2="$(echo $v | cut -d. -f2)"
+	local v3="$(echo $v | cut -d. -f3)"
+	[ -z "$v3" ] && v3=0
+
+	if [ $v1 -lt $r1 ] || [ $v1 -eq $r1 -a $v2 -lt $r2 ] || \
+		[ $v1 -eq $r1 -a $v2 -eq $r2 -a $v3 -lt $r3 ]; then
+		return 1
+	fi
+	return 0
+}
+
+# prints major version: 1: TPM 1.2, 2: TPM 2.0
+# or nothing on TPM-bypass (no TPM device)
+# WARNING: Detecting TPM 2.0 can fail due kernel not exporting TPM 2.0 files.
+get_tpm_version()
+{
+	if [ -f /sys/class/tpm/tpm0/tpm_version_major ]; then
+		cat /sys/class/tpm/tpm0/tpm_version_major
+		return
+	fi
+
+	if [ -f /sys/class/tpm/tpm0/device/caps -o \
+		-f /sys/class/misc/tpm0/device/caps ]; then
+		echo 1
+		return
+	fi
+
+	if [ -c /dev/tpmrm0 -a -c /dev/tpm0 ]; then
+		echo 2
+		return
+	fi
+
+	if [ ! -d /sys/class/tpm/tpm0/ -a ! -d /sys/class/misc/tpm0/ ]; then
+		return
+	fi
+
+	tst_require_cmds dmesg
+	if dmesg | grep -q 'activating TPM-bypass'; then
+		return
+	elif dmesg | grep -q '1\.2 TPM (device-id'; then
+		echo 1
+		return
+	elif dmesg | grep -q '2\.0 TPM (device-id'; then
+		echo 2
+		return
+	fi
+}
+
+test1_tpm_bypass_mode()
+{
+	local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}')
+
+	if [ "$DIGEST" = "$zero" ]; then
+		tst_res TPASS "bios boot aggregate is $zero"
 	else
-		boot_aggregate=$(ima_boot_aggregate -f $tpm_bios | grep "sha1:" | cut -d':' -f2)
-		if [ "$boot_hash" = "$boot_aggregate" ]; then
-			tst_res TPASS "bios aggregate matches IMA boot aggregate"
-		else
-			tst_res TFAIL "bios aggregate does not match IMA boot aggregate"
+		tst_res TFAIL "bios boot aggregate is not $zero ($DIGEST), kernel didn't export TPM 2.0 files for TPM device?"
+		return 1
+	fi
+}
+
+test1_hw_tpm()
+{
+	local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements"
+	local cmd="evmctl ima_boot_aggregate -v"
+	local boot_aggregate
+
+	[ -z "$TPM_VERSION" ] && \
+		tst_res TWARN "TPM-bypass failed, trying to test TPM device (unknown TPM version)"
+
+	if [ "$MISSING_EVMCTL" = 1 ]; then
+		if [ ! -f "$tpm_bios" ]; then
+			tst_res TCONF "missing $tpm_bios $ERRMSG_EVMCTL"
+			return
 		fi
+		tst_check_cmds ima_boot_aggregate || return
+		cmd="ima_boot_aggregate -f $tpm_bios"
 	fi
+	tst_res TINFO "using command: $cmd"
+
+	boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2)
+	if [ -z "$boot_aggregate" ]; then
+		tst_res TFAIL "failed to get boot aggregate"
+		return
+	fi
+	tst_res TINFO "IMA boot aggregate: '$boot_aggregate'"
+
+	if [ "$DIGEST" = "$boot_aggregate" ]; then
+		tst_res TPASS "bios boot aggregate matches IMA boot aggregate"
+	else
+		tst_res TFAIL "bios boot aggregate does not match IMA boot aggregate ($DIGEST)"
+	fi
+}
+
+test1()
+{
+	tst_res TINFO "verify boot aggregate"
+
+	# deliberately try test1_hw_tpm() if test1_tpm_bypass_mode() fails
+	[ -z "$TPM_VERSION" ] && test1_tpm_bypass_mode || test1_hw_tpm
 }
 
 # Probably cleaner to programmatically read the PCR values directly