[LTP] [PATCH v2 4/4] ima_tpm.sh: Fix calculating PCR aggregate

Petr Vorel
2020-09-25T18:50:34Z

for TPM 2.0 and never evmctl versions.

Because exporting PCR registers for TPM 2.0 has not been upstreamed [1],
we use user space code, which requires evmctl 1.3.1 and tsspcrread.
Using evmctl allows to test for TPM devices which does not export event
log (/sys/kernel/security/tpm0/binary_bios_measurements).

For TPM 1.2 read tpm pcrs file (tss1pcrread could be also used, but
it's not yet packaged by distros).

Add support for never evmctl versions (>= 1.3) and very old versions,
which has different output format for ima_measurement command:
* 1.3 adds prefix for algorithm and TPM, e.g.: "sha256: TPM PCR-10:"
* old versions (v1.0, v0.9) printed only "PCR-10:"
Previously supported "HW PCR-10:" was format for ima_measurement for
evmctl 1.1.

[1] https://patchwork.kernel.org/patch/11759729/

Signed-off-by: Petr Vorel <pvorel@suse.cz>
---
.../security/integrity/ima/tests/ima_tpm.sh | 145 ++++++++++++------
1 file changed, 102 insertions(+), 43 deletions(-)

diff --git a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
index 1fccc844b..78e81ed64 100755
--- a/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
+++ b/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh
@@ -14,6 +14,7 @@ TST_SETUP="setup"

EVMCTL_REQUIRED='1.3'
ERRMSG_EVMCTL="install evmctl >= $EVMCTL_REQUIRED"
+ERRMSG_TPM="TPM hardware support not enabled in kernel or no TPM chip found"

setup()
{
@@ -68,6 +69,83 @@ check_evmctl()
return 0
}

+# prints major version 1: TPM 1.2, 2: TPM 2.0
+# or nothing when version not detected
+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
+
+ tst_check_cmds dmesg || return
+ if dmesg | grep -q '1\.2 TPM (device-id'; then
+ echo 1
+ elif dmesg | grep -q '2\.0 TPM (device-id'; then
+ echo 2
+ fi
+}
+
+read_pcr_tpm1()
+{
+ local pcr_path="/sys/class/tpm/tpm0/device/pcrs"
+ local pcr hash
+
+ if [ ! -f "$pcrs_path" ]; then
+ pcrs_path="/sys/class/misc/tpm0/device/pcrs"
+ fi
+
+ if [ ! -f "$pcr_path" ]; then
+ tst_brk TCONF "missing PCR file $pcrs_path ($ERRMSG_TPM)"
+ fi
+
+ while read line; do
+ pcr="$(echo $line | cut -d':' -f1)"
+ hash="$(echo $line | cut -d':' -f2 | awk '{ gsub (" ", "", $0); print tolower($0) }')"
+ echo "$pcr: $hash"
+ done < $pcr_path
+}
+
+# NOTE: TPM 1.2 would require to use tss1pcrread which is not fully adopted
+# by distros yet.
+read_pcr_tpm2()
+{
+ local pcrmax=23
+ local pcrread="tsspcrread -halg $ALGORITHM"
+ local i pcr
+
+ tst_check_cmds tsspcrread || return 1
+
+ for i in $(seq 0 $pcrmax); do
+ pcr=$($pcrread -ha "$i" -ns)
+ if [ $? -ne 0 ]; then
+ tst_brk TBROK "tsspcrread failed: $pcr"
+ fi
+ printf "PCR-%02d: %s\n" $i "$pcr"
+ done
+}
+
+get_pcr10_aggregate()
+{
+ local pcr
+
+ if ! evmctl -v ima_measurement $BINARY_MEASUREMENTS > hash.txt 2>&1; then
+ tst_res TBROK "evmctl failed:"
+ cat hash.txt >&2
+ return 1
+ fi
+ pcr=$(grep -E "^($ALGORITHM: TPM |HW )*PCR-10:" hash.txt \
+ | awk '{print $NF}')
+
+ echo "$pcr"
+}
+
test1()
{
tst_res TINFO "verify boot aggregate"
@@ -88,7 +166,7 @@ test1()

boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2)
if [ -z "$boot_aggregate" ]; then
- tst_res TINFO "TPM hardware support not enabled in kernel or no TPM chip found"
+ tst_res TINFO "$ERRMSG_TPM"

zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}')
if [ "$DIGEST" = "$zero" ]; then
@@ -108,57 +186,38 @@ test1()
fi
}

-# Probably cleaner to programmatically read the PCR values directly
-# from the TPM, but that would require a TPM library. For now, use
-# the PCR values from /sys/devices.
-validate_pcr()
+test2()
{
- tst_res TINFO "verify PCR (Process Control Register)"
+ local hash pcr_aggregate

- local dev_pcrs="$1"
- local pcr hash aggregate_pcr
+ tst_res TINFO "verify PCR values"

- aggregate_pcr="$(evmctl -v ima_measurement $BINARY_MEASUREMENTS 2>&1 | \
- grep 'HW PCR-10:' | awk '{print $3}')"
- if [ -z "$aggregate_pcr" ]; then
- tst_res TFAIL "failed to get PCR-10"
- return 1
+ tpm_version="$(get_tpm_version)"
+ if [ -z "$tpm_version" ]; then
+ tst_brk TCONF "TMP version not detected ($ERRMSG_TPM)"
fi
+ tst_res TINFO "TMP major version: $tpm_version"

- while read line; do
- pcr="$(echo $line | cut -d':' -f1)"
- if [ "$pcr" = "PCR-10" ]; then
- hash="$(echo $line | cut -d':' -f2 | awk '{ gsub (" ", "", $0); print tolower($0) }')"
- [ "$hash" = "$aggregate_pcr" ]
- return $?
- fi
- done < $dev_pcrs
- return 1
-}
-
-test2()
-{
- tst_res TINFO "verify PCR values"
- tst_check_cmds evmctl || return
-
- tst_res TINFO "evmctl version: $(evmctl --version)"
+ read_pcr_tpm$tpm_version > pcr.txt
+ hash=$(grep "^PCR-10" pcr.txt | cut -d' ' -f2)
+ if [ -z "$hash" ]; then
+ tst_res TBROK "PCR-10 hash not found"
+ cat pcr.txt
+ return 1
+ fi
+ tst_res TINFO "real PCR-10: '$hash'"

- local pcrs_path="/sys/class/tpm/tpm0/device/pcrs"
- if [ -f "$pcrs_path" ]; then
- tst_res TINFO "new PCRS path, evmctl >= 1.1 required"
- else
- pcrs_path="/sys/class/misc/tpm0/device/pcrs"
+ pcr_aggregate="$(get_pcr10_aggregate)"
+ if [ -z "$pcr_aggregate" ]; then
+ tst_res TFAIL "failed to get aggregate PCR-10"
+ return
fi
+ tst_res TINFO "aggregate PCR-10: '$hash'"

- if [ -f "$pcrs_path" ]; then
- validate_pcr $pcrs_path
- if [ $? -eq 0 ]; then
- tst_res TPASS "aggregate PCR value matches real PCR value"
- else
- tst_res TFAIL "aggregate PCR value does not match real PCR value"
- fi
+ if [ "$hash" = "$pcr_aggregate" ]; then
+ tst_res TPASS "aggregate PCR value matches real PCR value"
else
- tst_res TCONF "TPM Hardware Support not enabled in kernel or no TPM chip found"
+ tst_res TFAIL "aggregate PCR value does not match real PCR value"
fi
}

--
2.28.0