swtpm/tests/test_tpm2_save_load_state_2
Stefan Berger aa483aeb6d swtpm: nvstore_linear: Add support for file-backend locking
Add support for locking the storage file using fcntl(fd, F_SETLK, ...).
Since fcntl needs a file descriptor of the actual storage file, call
SWTPM_NVRAM_LinearFile_DoOpenURI() to open the file in case it has not
been opened, yet. In case of error close the file again but be careful
about the fact that it may not have been mmap'ed, yet.

Since now all backends have .lock and .unlock nvram_backend_ops, they can
be called without checking for a NULL pointer.

Extend an existing test case with a file-backend storage lock test.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-10-15 10:49:01 -04:00

352 lines
9.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# For the license, see the LICENSE file in the root directory.
#set -x
if [ "${SWTPM_TEST_IBMTSS2:-0}" -eq 0 ]; then
echo "SWTPM_TEST_IBMTSS2 must be set to run this test."
exit 77
fi
if ! type -p nvdefinespace startup &>/dev/null; then
PREFIX=tss
if ! type -p ${PREFIX}nvdefinespace ${PREFIX}startup; then
echo "Could not find TPM2 tools (e.g., (tss)startup, (tss)nvdefinespace) in PATH."
exit 77
fi
fi
TOOLSPATH=$(dirname "$(type -P "${PREFIX}startup")")
ROOT=${abs_top_builddir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:-$(dirname "$0")}
TPMDIR="$(mktemp -d)" || exit 1
PID_FILE=$TPMDIR/swtpm.pid
SOCK_PATH=$TPMDIR/sock
LOGFILE=$TPMDIR/logfile
LOGFILE2=$TPMDIR/logfile2
TMPFILE=$TPMDIR/tmpfile
BINFILE=$TPMDIR/binfile
SIGFILE=$TPMDIR/sigfile
STATEFILE=${STATEFILE:-$TPMDIR/state}
STORE_PARAM="dir=$TPMDIR"
if [ "${SWTPM_TEST_LINEAR_FILE:-0}" -ne 0 ]; then
echo "Testing with linear file backend ($STATEFILE)"
STORE_PARAM="backend-uri=file://$STATEFILE"
fi
source "${TESTDIR}/test_common"
source "${TESTDIR}/common"
skip_test_no_tpm20 "${SWTPM_EXE}"
trap "cleanup" SIGTERM EXIT
function cleanup()
{
rm -rf "$TPMDIR"
# remove files from tss tools
rm -f h01*.bin nvp*.bin
if [ -n "$PID" ]; then
kill_quiet -SIGTERM "$PID" 2>/dev/null
fi
}
# Fill up the NVRAM space with 2048 bit signing keys and then an NVRAM area that
# fills it up to the last byte. We want to make sure that the OBJECTs that the
# RSA keys are creating in NVRAM can be loaded into the NVRAM again when the size
# of the OBJECT increases when for example the size of the RSA keys increases.
# This may force us to increase the NVRAM memory space in libtpms then.
function fillup_nvram()
{
local create="$1"
local check="$2"
local i sz
if [ "$create" -eq 1 ]; then
# Fill up the NVRAM space with RSA 2048 keys;
# exactly 65 have to fit
if ! "${TOOLSPATH}/${PREFIX}createprimary" -hi o -si > "$TMPFILE"; then
echo "Error: createprimary failed."
exit 1
fi
if ! grep -q 80000000 "$TMPFILE"; then
echo "Error: createprimary did not result in expected handle 80000000"
exit 1
fi
for ((i = 0x81000000; i < 0x81000100; i++)); do
"${TOOLSPATH}/${PREFIX}evictcontrol" \
-hi o \
-ho 80000000 \
-hp "$(printf "%x" "$i")" &>"$TMPFILE" || break
done
"${TOOLSPATH}/${PREFIX}getcapability" -cap 1 -pr 81000000 -pc 80 > "$TMPFILE"
# We need know we need to see '65 Handles' for state created with
# libtpms-0.6.0 and 128kb NVRAM size
if ! grep -i "65 Handles" "$TMPFILE"; then
echo "Error: Did not find '65 Handles' keyword in output"
cat "$TMPFILE"
exit 1
fi
# Fill up the rest of the NVRAM with a single NVRAM index whose size
# we now have to find;
# for reference: libtpms v0.6.0 allowed 236 bytes
for ((sz = 0; ; sz++)); do
"${TOOLSPATH}/${PREFIX}nvdefinespace" \
-hi o \
-ha 01000000 \
-sz "${sz}" > "${TMPFILE}" || break
# this worked, so lets remove it and try the next size
#echo "NVRAM space of size $sz could be created"
"${TOOLSPATH}/${PREFIX}nvundefinespace" \
-hi o \
-ha 01000000 > "${TMPFILE}"
done
if [ "$sz" -gt 0 ]; then
sz=$((sz - 1))
echo "Creating final space of size ${sz}"
if ! "${TOOLSPATH}/${PREFIX}nvdefinespace" \
-hi o \
-ha 01000000 \
-sz ${sz} > "${TMPFILE}";
then
echo "Error: Could not create final NVRAM space."
cat "${TMPFILE}"
exit 1
fi
fi
if [ $sz -eq 0 ]; then
echo "Error: NVRAM space could not be created at all; not enough space!"
exit 1
elif [ $sz -lt 236 ]; then
echo "Error: Insufficient NVRAM memory. Needed to create an NVRAM index with size 236 bytes."
exit 1
elif [ $sz -gt 236 ]; then
echo "Error: The NVRAM index is too large. Only needed 236 bytes but got $sz bytes."
exit 1
else
echo "The NVRAM index is exactly of the right size (236 bytes)."
fi
echo -n "123" > "$BINFILE"
if ! "${TOOLSPATH}/${PREFIX}sign" \
-hk 81000000 \
-if "${BINFILE}" \
-os "${SIGFILE}" > "$TMPFILE";
then
echo "Error: Could not create signature."
cat "$TMPFILE"
exit 1
fi
fi
if [ "$check" -eq 1 ]; then
"${TOOLSPATH}/${PREFIX}getcapability" -cap 1 -pr 81000000 -pc 80 > "$TMPFILE"
# We need know we need to see '65 Handles' for state created with
# libtpms-0.6.0 and 128kb NVRAM size
if ! grep -i "65 Handles" "$TMPFILE"; then
echo "Error: Did not find '65 Handles' keyword in output"
cat "$TMPFILE"
exit 1
fi
printf "Verifying signature with all the persisted keys\n"
echo -n "123" > "$BINFILE"
for ((i = 0x81000000; i < 0x81000040; i++)); do
if ! "${TOOLSPATH}/${PREFIX}verifysignature" \
-hk "$(printf "%x" "$i")" \
-is "${SIGFILE}" \
-if "${BINFILE}" > "$TMPFILE";
then
echo "Verifying signature failed for handle $(printf "%x" "$i")."
exit 1
fi
done
fi
}
export TPM_SERVER_TYPE=raw
export TPM_SERVER_NAME=127.0.0.1
export TPM_INTERFACE_TYPE=socsim
export TPM_COMMAND_PORT=${TPM_COMMAND_PORT:-65460}
export TPM_DATA_DIR=$TPMDIR
export TPM_SESSION_ENCKEY="807e2bfe898ddaed8fa6310e716a24dc" # for sessions
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "$STORE_PARAM" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (1) Socket TPM did not write pidfile."
exit 1
fi
PID="$(cat "$PID_FILE")"
# Send TPM_Init
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -i 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_INIT failed: $act"
exit 1
fi
if ! "${TOOLSPATH}/${PREFIX}startup" -c; then
echo "Error: tpm_startup clear failed."
exit 1
fi
fillup_nvram 1 1
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
if wait_process_gone "${PID}" 4; then
echo "Error: swtpm did not shut down"
exit 1
fi
echo "============================" >> "$LOGFILE"
echo "TPM was shut down"
# Store this state for later usage; use a really old version of libtpms: 0.6.0
#cp $TPMDIR/tpm2-00.permall ${TESTDIR}/data/tpm2state5;
#cp $SIGFILE ${TESTDIR}/data/tpm2state5/signature.bin
#################################################################
# Run TPM2 with the created state and verify it's the same
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "$STORE_PARAM" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (2) Socket TPM did not write pidfile."
exit 1
fi
echo "TPM re-started"
PID="$(cat "$PID_FILE")"
# Send TPM_Init
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -i 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_INIT failed: $act"
cat "$LOGFILE"
exit 1
fi
if ! "${TOOLSPATH}/${PREFIX}startup" -c; then
echo "Error: tpm_startup clear failed."
cat "$LOGFILE"
exit 1
fi
fillup_nvram 0 1
if ! "${TOOLSPATH}/${PREFIX}shutdown" -c; then
echo "Error: tpm_shutdown clear failed."
cat "$LOGFILE"
exit 1
fi
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
echo "============================" >> "$LOGFILE"
echo "TPM was shut down"
#################################################################
# Run TPM2 with previously saved state and verify it's the same
if [ "${SWTPM_TEST_LINEAR_FILE:-0}" -ne 0 ]; then
echo "Test 1 OK (skipped last with linear file)"
exit 0
fi
rm -f "$TPMDIR"/*
cp -f "${TESTDIR}/data/tpm2state5/tpm2-00.permall" "$TPMDIR/tpm2-00.permall"
cp "${TESTDIR}/data/tpm2state5/signature.bin" "$SIGFILE"
$SWTPM_EXE socket \
--server "port=${TPM_COMMAND_PORT}" \
--tpmstate "$STORE_PARAM,lock" \
--pid "file=$PID_FILE" \
--ctrl "type=unixio,path=$SOCK_PATH" \
--log "file=$LOGFILE,level=20" \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}} &
if wait_for_file "$PID_FILE" 3; then
echo "Error: (3) Socket TPM did not write pidfile."
exit 1
fi
echo "TPM started with previously generated state"
PID="$(cat "$PID_FILE")"
# Send TPM_Init
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -i 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_INIT failed: $act"
exit 1
fi
if ! "${TOOLSPATH}/${PREFIX}startup" -c; then
echo "Error: tpm_startup clear failed."
cat "$LOGFILE"
exit 1
fi
fillup_nvram 0 1
if ! "${TOOLSPATH}/${PREFIX}shutdown" -c; then
echo "Error: tpm_shutdown clear failed."
cat "$LOGFILE"
exit 1
fi
# Start another swtpm that must fail to start due to locked storage
# The not-need-init flag causes the failure.
if $SWTPM_EXE socket \
--tpmstate "$STORE_PARAM,lock" \
--log "file=${LOGFILE2},level=1" \
--flags not-need-init \
--tpm2 \
${SWTPM_TEST_SECCOMP_OPT:+${SWTPM_TEST_SECCOMP_OPT}}; then
echo "Error: Starting 2nd instance on locked storage should have failed"
exit 1
fi
if ! grep -q "Resource temporarily unavailable" "${LOGFILE2}"; then
echo "Error: Expected error 'Resource temporarily unavailable' in logfile"
cat "${LOGFILE2}"
exit 1
fi
# Send Shutdown
if ! act=$($SWTPM_IOCTL --unix "$SOCK_PATH" -s 2>&1); then
echo "Error: $SWTPM_IOCTL CMD_SHUTDOWN failed: $act"
exit 1
fi
echo "Test 1 OK"