]> git.proxmox.com Git - swtpm.git/commitdiff
tests: Add test case to check that swtpm sends a TPM2_Shutdown
authorStefan Berger <stefanb@linux.ibm.com>
Wed, 1 Jun 2022 12:44:00 +0000 (08:44 -0400)
committerStefan Berger <stefanb@us.ibm.com>
Thu, 18 Aug 2022 13:50:16 +0000 (09:50 -0400)
Add a test case that checks that swtpm sends a TPM2_Shutdown() to the
TPM 2 upon abrupt re-initialization (CMD_INIT) or graceful shutdown
(control channel, CMD_SHUTDOWN) of the TPM 2 and avoids a potential
dictionary attack (DA) lock-out. A previously sent command failing
authorization with DA implications would otherwise trigger the
TPM_PT_LOCKOUT_COUNTER to increase by '1' if the TPM 2 was not properly
shut down by the client (guest OS) with a TPM2_Shutdown() command.

The test case tests whether a TPM2_Shutdown() is now sent before a reset.
The defined password-protected NVRAM area has the DA flag set and the test
case tries to read from it without providing a password. If we didn't send
the TPM2_Shutdown() before the test cases sends the reset (CMD_INIT), then
the dictionary attack lockout counter would be increased by one. With the
instrumentation in the previous patch the automatically sent
TPM2_Shutdown() keeps the counter at 0.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
tests/Makefile.am
tests/_test_tpm2_avoid_da_lockout [new file with mode: 0755]
tests/test_tpm2_avoid_da_lockout [new file with mode: 0755]

index f076699ba01d81e9b442e0a0835c8a6053837313..eb9dae39ea1d800fa96cbf773ed4ba55cada026c 100644 (file)
@@ -46,6 +46,7 @@ TESTS += \
        test_swtpm_setup_misc
 
 TESTS += \
+       test_tpm2_avoid_da_lockout \
        test_tpm2_ctrlchannel2 \
        test_tpm2_derived_keys \
        test_tpm2_encrypted_state \
@@ -184,6 +185,7 @@ EXTRA_DIST=$(TESTS) \
        _test_setbuffersize \
        _test_swtpm_bios \
        _test_tpm_probe \
+       _test_tpm2_avoid_da_lockout \
        _test_tpm2_derived_keys \
        _test_tpm2_encrypted_state \
        _test_tpm2_file_permissions \
diff --git a/tests/_test_tpm2_avoid_da_lockout b/tests/_test_tpm2_avoid_da_lockout
new file mode 100755 (executable)
index 0000000..da59226
--- /dev/null
@@ -0,0 +1,164 @@
+#!/bin/bash
+
+# For the license, see the LICENSE file in the root directory.
+#set -x
+
+ROOT=${abs_top_builddir:-$(pwd)/..}
+TESTDIR=${abs_top_testdir:-$(dirname "$0")}
+
+VTPM_NAME="vtpm-test-tpm2-avoid-da-lockout"
+SWTPM_DEV_NAME="/dev/${VTPM_NAME}"
+export TPM_PATH="$(mktemp -d)" || exit 1
+LOG_FILE=$TPM_PATH/tpm-00.log
+SWTPM_CMD_UNIX_PATH=${TPM_PATH}/unix-cmd.sock
+SWTPM_CTRL_UNIX_PATH=${TPM_PATH}/unix-ctrl.sock
+
+function cleanup()
+{
+       pid=${SWTPM_PID}
+       if [ -n "$pid" ]; then
+               kill_quiet -9 $pid
+       fi
+       rm -rf $TPM_PATH
+}
+
+trap "cleanup" EXIT
+
+[ "${SWTPM_INTERFACE}" == "cuse" ] && source ${TESTDIR}/test_cuse
+source ${TESTDIR}/common
+source ${TESTDIR}/test_common
+
+run_swtpm ${SWTPM_INTERFACE} \
+       --tpm2 \
+       --log file=$LOG_FILE,level=20 \
+       --flags not-need-init,startup-clear
+
+kill_quiet -0 ${SWTPM_PID}
+if [ $? -ne 0 ]; then
+       echo "Error: ${SWTPM_INTERFACE} TPM did not start."
+       exit 1
+fi
+
+# Define password-protected NV space with DA attribute set: tssnvdefinespace -ha 01000000 -hi o -pwdn test -sz 1 -at da
+cmd='\x80\x02\x00\x00\x00\x31\x00\x00\x01\x2a\x40\x00\x00\x01\x00\x00\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x04\x74\x65\x73\x74\x00\x0e\x01\x00\x00\x00\x00\x0b\x00\x04\x00\x04\x00\x00\x00\x01'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 02 00 00 00 13 00 00 00 00 00 00 00 00 00 00 01 00 00'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_NV_DefineSpace"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+# Write to NV space without password: tssnvwrite -ha 01000000 -ic A
+cmd='\x80\x02\x00\x00\x00\x24\x00\x00\x01\x37\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x01\x41\x00\x00'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 01 00 00 00 0a 00 00 09 22'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_NV_Write"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+# The TPM_PT_LOCKOUT_COUNTER must be 0 now: tssgetcapability -cap 6 -pr 0x20e -pc 1
+cmd='\x80\x01\x00\x00\x00\x16\x00\x00\x01\x7a\x00\x00\x00\x06\x00\x00\x02\x0e\x00\x00\x00\x01'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 01 00 00 00 1b 00 00 00 00 01 00 00 00 06 00 00 00 01 00 00 02 0e 00 00 00 00'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_GetCapability(TPM_PT_LOCKOUT_COUNTER)"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+# Abruptly init the TPM: swtpm will internally send TPM2_Shutdown()
+run_swtpm_ioctl ${SWTPM_INTERFACE} -i
+if [ $? -ne 0 ]; then
+       echo "Error: Could not initialize the ${SWTPM_INTERFACE} TPM."
+       exit 1
+fi
+
+# send TPM2_Startup(SU_CLEAR)
+cmd='\x80\x01\x00\x00\x00\x0c\x00\x00\x01\x44\x00\x00'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 01 00 00 00 0a 00 00 00 00'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_Startup(SU_CLEAR)"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+# The TPM_PT_LOCKOUT_COUNTER must still be '0' now: tssgetcapability -cap 6 -pr 0x20e -pc 1
+# Without swtpm sending TPM2_Shutdown, it would be '1' now
+cmd='\x80\x01\x00\x00\x00\x16\x00\x00\x01\x7a\x00\x00\x00\x06\x00\x00\x02\x0e\x00\x00\x00\x01'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 01 00 00 00 1b 00 00 00 00 01 00 00 00 06 00 00 00 01 00 00 02 0e 00 00 00 00'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_GetCapability(TPM_PT_LOCKOUT_COUNTER)"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+# Again write to NV space without password: tssnvwrite -ha 01000000 -ic A
+cmd='\x80\x02\x00\x00\x00\x24\x00\x00\x01\x37\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x09\x40\x00\x00\x09\x00\x00\x00\x00\x00\x00\x01\x41\x00\x00'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 01 00 00 00 0a 00 00 09 22'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_NV_Write"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+# CMD_STOP: swtpm will internally send TPM2_Shutdown()
+run_swtpm_ioctl ${SWTPM_INTERFACE} -s
+if [ $? -ne 0 ]; then
+       echo "Error: Could not shut down the ${SWTPM_INTERFACE} TPM."
+       exit 1
+fi
+
+if wait_process_gone ${SWTPM_PID} 4; then
+       echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore."
+       exit 1
+fi
+
+run_swtpm ${SWTPM_INTERFACE} \
+       --tpm2 \
+       --log file=$LOG_FILE,level=20 \
+       --flags not-need-init,startup-clear
+
+kill_quiet -0 ${SWTPM_PID}
+if [ $? -ne 0 ]; then
+       echo "Error: ${SWTPM_INTERFACE} TPM did not start."
+       exit 1
+fi
+
+# The TPM_PT_LOCKOUT_COUNTER must still be '0' now: tssgetcapability -cap 6 -pr 0x20e -pc 1
+# Without swtpm sending TPM2_Shutdown, it would be '2' now
+cmd='\x80\x01\x00\x00\x00\x16\x00\x00\x01\x7a\x00\x00\x00\x06\x00\x00\x02\x0e\x00\x00\x00\x01'
+RES=$(swtpm_cmd_tx ${SWTPM_INTERFACE} ${cmd})
+exp=' 80 01 00 00 00 1b 00 00 00 00 01 00 00 00 06 00 00 00 01 00 00 02 0e 00 00 00 00'
+if [ "$RES" != "$exp" ]; then
+       echo "Error: Did not get expected result from TPM2_GetCapability(TPM_PT_LOCKOUT_COUNTER)"
+       echo "expected: $exp"
+       echo "received: $RES"
+       exit 1
+fi
+
+run_swtpm_ioctl ${SWTPM_INTERFACE} -s
+if [ $? -ne 0 ]; then
+       echo "Error: Could not shut down the ${SWTPM_INTERFACE} TPM."
+       exit 1
+fi
+
+if wait_process_gone ${SWTPM_PID} 4; then
+       echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore."
+       exit 1
+fi
+
+echo "OK"
+
+exit 0
diff --git a/tests/test_tpm2_avoid_da_lockout b/tests/test_tpm2_avoid_da_lockout
new file mode 100755 (executable)
index 0000000..ea1fabd
--- /dev/null
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+
+TESTDIR=${abs_top_testdir:-$(dirname "$0")}
+ROOT=${abs_top_builddir:-$(dirname "$0")/..}
+source ${TESTDIR}/common
+skip_test_no_tpm20 "${SWTPM_EXE}"
+
+cd "$(dirname "$0")"
+
+export SWTPM_IOCTL_BUFFERSIZE=100
+export SWTPM_INTERFACE=cuse
+bash _test_tpm2_avoid_da_lockout
+ret=$?
+[ $ret -ne 0  ] && [ $ret -ne 77 ] && exit $ret
+
+export SWTPM_INTERFACE=socket+socket
+export SWTPM_SERVER_NAME=localhost
+export SWTPM_SERVER_PORT=65464
+export SWTPM_CTRL_PORT=65465
+bash _test_tpm2_avoid_da_lockout
+ret=$?
+[ $ret -ne 0  ] && [ $ret -ne 77 ] && exit $ret
+
+export SWTPM_INTERFACE=socket+unix
+export SWTPM_SERVER_NAME=localhost
+export SWTPM_SERVER_PORT=65464
+bash _test_tpm2_avoid_da_lockout
+ret=$?
+[ $ret -ne 0  ] && [ $ret -ne 77 ] && exit $ret
+
+export SWTPM_INTERFACE=unix+unix
+bash _test_tpm2_avoid_da_lockout
+ret=$?
+[ $ret -ne 0  ] && [ $ret -ne 77 ] && exit $ret
+
+exit 0