From 412ccf6e972e1166a2a179b137cfdeeea00cc423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Old=C5=99ich=20Jedli=C4=8Dka?= Date: Wed, 2 Oct 2024 22:56:13 +0200 Subject: [PATCH] tpm2: added TPM 2 software TPM tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Oldřich Jedlička --- .github/workflows/install-dependencies | 2 +- src/pins/tpm2/meson.build | 20 +-- src/pins/tpm2/tests/meson.build | 42 ++++++ src/pins/tpm2/tests/pin-tpm2-hw | 26 ++++ src/pins/tpm2/tests/pin-tpm2-sw | 27 ++++ .../tpm2/{pin-tpm2 => tests/pin-tpm2-tests} | 61 +------- .../tpm2/tests/tpm2-common-test-functions.in | 135 ++++++++++++++++++ 7 files changed, 234 insertions(+), 79 deletions(-) create mode 100644 src/pins/tpm2/tests/meson.build create mode 100755 src/pins/tpm2/tests/pin-tpm2-hw create mode 100755 src/pins/tpm2/tests/pin-tpm2-sw rename src/pins/tpm2/{pin-tpm2 => tests/pin-tpm2-tests} (74%) create mode 100644 src/pins/tpm2/tests/tpm2-common-test-functions.in diff --git a/.github/workflows/install-dependencies b/.github/workflows/install-dependencies index e8eaa4fd..42f7e7db 100755 --- a/.github/workflows/install-dependencies +++ b/.github/workflows/install-dependencies @@ -24,7 +24,7 @@ debian:*|ubuntu:*) build-essential pkg-config libssl-dev libjansson-dev libjose-dev \ luksmeta libluksmeta-dev libpwquality-tools libglib2.0-dev \ libudisks2-dev libaudit-dev systemd opensc pcscd libsofthsm2-dev \ - swtpm-tools tpm-tools; do + swtpm-tools tpm-tools tpm2-tools; do sleep 5 done ;; diff --git a/src/pins/tpm2/meson.build b/src/pins/tpm2/meson.build index ec01dcf9..19077106 100644 --- a/src/pins/tpm2/meson.build +++ b/src/pins/tpm2/meson.build @@ -11,25 +11,7 @@ if all bins += join_paths(meson.current_source_dir(), 'clevis-decrypt-tpm2') bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-tpm2') mans += join_paths(meson.current_source_dir(), 'clevis-encrypt-tpm2.1') + subdir('tests') else warning('Will not install tpm2 pin due to missing dependencies!') endif - -# Tests. -env = environment() -env.prepend('PATH', - join_paths(meson.source_root(), 'src'), - join_paths(meson.source_root(), 'src', 'luks'), - join_paths(meson.source_root(), 'src', 'luks', 'tests'), - join_paths(meson.source_root(), 'src', 'pins', 'sss'), - join_paths(meson.source_root(), 'src', 'pins', 'tang'), - join_paths(meson.source_root(), 'src', 'pins', 'tpm2'), - join_paths(meson.build_root(), 'src'), - join_paths(meson.build_root(), 'src', 'luks'), - join_paths(meson.build_root(), 'src', 'luks', 'tests'), - join_paths(meson.build_root(), 'src', 'pins', 'sss'), - join_paths(meson.build_root(), 'src', 'pins', 'tang'), - join_paths(meson.build_root(), 'src', 'pins', 'tpm2'), - separator: ':' -) -test('pin-tpm2', find_program('pin-tpm2'), env: env, timeout: 120) diff --git a/src/pins/tpm2/tests/meson.build b/src/pins/tpm2/tests/meson.build new file mode 100644 index 00000000..e63cfbd6 --- /dev/null +++ b/src/pins/tpm2/tests/meson.build @@ -0,0 +1,42 @@ +# Tests. +env = environment() +env.prepend('PATH', + join_paths(meson.source_root(), 'src'), + join_paths(meson.source_root(), 'src', 'luks'), + join_paths(meson.source_root(), 'src', 'luks', 'tests'), + join_paths(meson.source_root(), 'src', 'pins', 'sss'), + join_paths(meson.source_root(), 'src', 'pins', 'tang'), + join_paths(meson.source_root(), 'src', 'pins', 'tpm2'), + join_paths(meson.source_root(), 'src', 'pins', 'tpm2', 'tests'), + join_paths(meson.build_root(), 'src'), + join_paths(meson.build_root(), 'src', 'luks'), + join_paths(meson.build_root(), 'src', 'luks', 'tests'), + join_paths(meson.build_root(), 'src', 'pins', 'sss'), + join_paths(meson.build_root(), 'src', 'pins', 'tang'), + join_paths(meson.build_root(), 'src', 'pins', 'tpm2'), + join_paths(meson.build_root(), 'src', 'pins', 'tpm2', 'tests'), + separator: ':' +) + +tpm2_data = configuration_data() +tpm2_data.merge_from(data) + +socat = find_program('socat', required: false) +swtpm = find_program('swtpm', '/usr/bin/swtpm', required: false) +swtpm_setup = find_program('swtpm_setup', '/usr/bin/swtpm_setup', required: false) +swtpm_bios = find_program('swtpm_bios', '/usr/bin/swtpm_bios', required: false) + + +tpm2_data.set('SOCAT_BIN', socat.found() ? socat.path() : '') +tpm2_data.set('SWTPM_BIN', swtpm.found() ? swtpm.path() : '') +tpm2_data.set('SWTPM_SETUP_BIN', swtpm_setup.found() ? swtpm_setup.path() : '') +tpm2_data.set('SWTPM_BIOS_BIN', swtpm_bios.found() ? swtpm_bios.path() : '') + +configure_file( + input: 'tpm2-common-test-functions.in', + output: 'tpm2-common-test-functions', + configuration: tpm2_data, +) + +test('pin-tpm2-hw', find_program('pin-tpm2-hw'), env: env, timeout: 120) +test('pin-tpm2-sw', find_program('pin-tpm2-sw'), env: env, timeout: 120) diff --git a/src/pins/tpm2/tests/pin-tpm2-hw b/src/pins/tpm2/tests/pin-tpm2-hw new file mode 100755 index 00000000..2c444d13 --- /dev/null +++ b/src/pins/tpm2/tests/pin-tpm2-hw @@ -0,0 +1,26 @@ +#!/bin/bash -x +# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2019 Red Hat, Inc. +# Author: Sergio Correia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +. tpm2-common-test-functions + +tpm2_hw_available || skip_test +tpm2_version || skip_test + +. pin-tpm2-tests diff --git a/src/pins/tpm2/tests/pin-tpm2-sw b/src/pins/tpm2/tests/pin-tpm2-sw new file mode 100755 index 00000000..8b64c136 --- /dev/null +++ b/src/pins/tpm2/tests/pin-tpm2-sw @@ -0,0 +1,27 @@ +#!/bin/bash -x +# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2019 Red Hat, Inc. +# Author: Sergio Correia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +. tpm2-common-test-functions + +tpm2_sw_check_preconditions +tpm2_version || skip_test +tpm2_start_emulation + +. pin-tpm2-tests diff --git a/src/pins/tpm2/pin-tpm2 b/src/pins/tpm2/tests/pin-tpm2-tests similarity index 74% rename from src/pins/tpm2/pin-tpm2 rename to src/pins/tpm2/tests/pin-tpm2-tests index 5ef0a6a3..a9154ca2 100755 --- a/src/pins/tpm2/pin-tpm2 +++ b/src/pins/tpm2/tests/pin-tpm2-tests @@ -20,43 +20,6 @@ TEST=$(basename "${0}") -# Code to return to mark test as skipped. -SKIP_RET_CODE=77 - -tpm2_available() { - # Old environment variables for tpm2-tools 3.0 - export TPM2TOOLS_TCTI_NAME=device - export TPM2TOOLS_DEVICE_FILE= - for dev in /dev/tpmrm?; do - [ -e "${dev}" ] || continue - TPM2TOOLS_DEVICE_FILE="${dev}" - break - done - - # New environment variable for tpm2-tools >= 3.1 - export TPM2TOOLS_TCTI="${TPM2TOOLS_TCTI_NAME}:${TPM2TOOLS_DEVICE_FILE}" - - if [ -z "${TPM2TOOLS_DEVICE_FILE}" ]; then - echo "A TPM2 device with the in-kernel resource manager is needed!" >&2 - return 1 - fi - - if ! [[ -r "${TPM2TOOLS_DEVICE_FILE}" \ - && -w "${TPM2TOOLS_DEVICE_FILE}" ]]; then - echo "The ${TPM2TOOLS_DEVICE_FILE} device must be readable and writable!" >&2 - return 1 - fi - - local _tpm2tools_info="$(tpm2_createprimary -v)" - local _match='version="(.)\.' - [[ ${_tpm2tools_info} =~ ${_match} ]] && TPM2TOOLS_VERSION="${BASH_REMATCH[1]}" - if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then - echo "The tpm2 pin requires a tpm2-tools version between 3 and 5" >&2 - return 1 - fi - export TPM2TOOLS_VERSION -} - validate_pcrs() { local _pcr_bank="${1}" local _pcrs="${2}" @@ -77,11 +40,6 @@ validate_pcrs() { return 0 } -# Checking if we can run this test. -if ! tpm2_available; then - exit ${SKIP_RET_CODE} -fi - decode_jwe() { local jwe="${1}" @@ -160,32 +118,17 @@ else fi # Test with policies if we have the PIN rewrite available -if ! $(which clevis-pin-tpm2 >/dev/null 2>&1); +if ! command -v clevis-pin-tpm2 >/dev/null 2>&1; then echo "No PIN rewrite available" exit 0 fi -if ! $(which clevis-pin-tpm2-signtool >/dev/null 2>&1); +if ! command -v clevis-pin-tpm2-signtool >/dev/null 2>&1; then echo "No policy signtool available" exit 0 fi -function on_exit() { - popd - if [ ! -d "$TMP" ] || ! rm -rf "$TMP"; then - echo "Delete temporary files failed!" >&2 - echo "You need to clean up: $TMP" >&2 - exit 1 - fi -} -if ! TMP="$(mktemp -d)"; then - echo "Creating a temporary dir for TPM files failed!" >&2 - exit 1 -fi -trap 'on_exit' EXIT -pushd $TMP - clevis-pin-tpm2-signtool >policy_working.json << EOP --- - policy_ref: diff --git a/src/pins/tpm2/tests/tpm2-common-test-functions.in b/src/pins/tpm2/tests/tpm2-common-test-functions.in new file mode 100644 index 00000000..c05f9033 --- /dev/null +++ b/src/pins/tpm2/tests/tpm2-common-test-functions.in @@ -0,0 +1,135 @@ +#!/bin/bash -x +# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: +# +# Copyright (c) 2019 Red Hat, Inc. +# Author: Sergio Correia +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +. tests-common-functions + +SOCAT_BIN="@SOCAT_BIN@" +SWTPM_BIN="@SWTPM_BIN@" +SWTPM_SETUP_BIN="@SWTPM_SETUP_BIN@" +SWTPM_BIOS_BIN="@SWTPM_BIOS_BIN@" + +SWTPM_SOCKET_PID= +SOCAT_SERVER_PID= +SOCAT_CONTROL_PID= + +function on_exit() { + popd || error "Unable to change directory" + if [ ! -d "$TESTDIR" ] || ! rm -rf "$TESTDIR"; then + echo "Delete temporary files failed!" >&2 + echo "You need to clean up: $TESTDIR" >&2 + exit 1 + fi + + # Cleanup sw emulation + [ -n "$SOCAT_SERVER_PID" ] && kill $SOCAT_SERVER_PID >/dev/null 2>&1 + [ -n "$SOCAT_CONTROL_PID" ] && kill $SOCAT_CONTROL_PID >/dev/null 2>&1 + if [ -n "$SWTPM_SOCKET_PID" ]; then + kill $SWTPM_SOCKET_PID >/dev/null 2>&1 + sleep .5 + # swtpm does not always terminate gracefully, so kill it + kill -9 $SWTPM_SOCKET_PID >/dev/null 2>&1 + fi +} +if ! TESTDIR="$(mktemp -d)"; then + echo "Creating a temporary dir for TPM files failed!" >&2 + exit 1 +fi +trap 'on_exit' EXIT +pushd "$TESTDIR" || error "Unable to change directory" + + +tpm2_hw_available() { + # Old environment variables for tpm2-tools 3.0 + export TPM2TOOLS_TCTI_NAME=device + export TPM2TOOLS_DEVICE_FILE= + for dev in /dev/tpmrm?; do + [ -e "${dev}" ] || continue + TPM2TOOLS_DEVICE_FILE="${dev}" + break + done + + # New environment variable for tpm2-tools >= 3.1 + export TPM2TOOLS_TCTI="${TPM2TOOLS_TCTI_NAME}:${TPM2TOOLS_DEVICE_FILE}" + + if [ -z "${TPM2TOOLS_DEVICE_FILE}" ]; then + echo "A TPM2 device with the in-kernel resource manager is needed!" >&2 + return 1 + fi + + if ! [[ -r "${TPM2TOOLS_DEVICE_FILE}" \ + && -w "${TPM2TOOLS_DEVICE_FILE}" ]]; then + echo "The ${TPM2TOOLS_DEVICE_FILE} device must be readable and writable!" >&2 + return 1 + fi + return 0 +} + +tpm2_version() { + local _tpm2tools_info + local _match='version="(.)\.' + _tpm2tools_info="$(tpm2_createprimary -v)" + [[ ${_tpm2tools_info} =~ ${_match} ]] && TPM2TOOLS_VERSION="${BASH_REMATCH[1]}" + if [[ $TPM2TOOLS_VERSION -lt 3 ]] || [[ $TPM2TOOLS_VERSION -gt 5 ]]; then + echo "The tpm2 pin requires a tpm2-tools version between 3 and 5" >&2 + return 1 + fi + export TPM2TOOLS_VERSION +} + +tpm2_sw_check_preconditions() { + [ -x "${SWTPM_BIN}" ] || skip_test "Skipping TPM2 test with software emulation, swtpm not found" + [ -x "${SWTPM_SETUP_BIN}" ] || skip_test "Skipping TPM2 test with software emulation, swtpm_setup not found" + [ -x "${SWTPM_BIOS_BIN}" ] || skip_test "Skipping TPM2 test with software emulation, swtpm_bios not found" + + if ! "${SWTPM_BIN}" socket --print-capabilities | jq -e '(.version | test("^0\\.[0-6](\\..*)?$")) or (.features | index("tpm-2.0"))' >/dev/null 2>&1; then + skip_test "Skipping TPM2 test with software emulation, no support for TPM 2.0 in swtpm" + fi +} + +tpm2_start_emulation() { + local socket_wait + local server_sock + local control_sock + + echo "Starting TPM 2 emulation" >&2 + + # Setup TPM 2 data + "${SWTPM_SETUP_BIN}" --tpm-state "$TESTDIR" --tpm2 --create-ek-cert --create-platform-cert --lock-nvram --display >&2 || error "Unable to setup TPM 2 emulation" + + # Start emulation over socket + server_sock="$TESTDIR"/swtpm.server.sock + control_sock="$TESTDIR"/swtpm.ctrl.sock + "${SWTPM_BIN}" socket --tpmstate dir="$TESTDIR" --tpm2 --ctrl type=unixio,path="$control_sock" --server type=unixio,path="$server_sock" --flags not-need-init >&2 & + SWTPM_SOCKET_PID=$! + + socket_wait=1 + while [ $socket_wait -le 100 ]; do + [ -S "$server_sock" ] && break + socket_wait=$((socket_wait + 1)) + sleep 0.1 + done + [ "$socket_wait" -gt 100 ] && error "Unable to start TPM 2 emulation" + + # Run BIOS checks + "${SWTPM_BIOS_BIN}" --tpm2 --unix "$server_sock" || error "Unable to prepare TPM 2 emulation" + + # Use swtpm in tpm2-tools + export TPM2TOOLS_TCTI="cmd:\"$SOCAT_BIN\" - \"UNIX-CONNECT:$server_sock\"" +}