diff options
author | Stefan Berger <stefanb@linux.ibm.com> | 2018-11-14 10:07:08 -0500 |
---|---|---|
committer | Stefan Berger <stefanb@linux.vnet.ibm.com> | 2018-11-16 07:40:57 -0500 |
commit | 992929a38f9c1889be1dfe9bb64bff5950291a27 (patch) | |
tree | 36511bf235908cf591df431dda499722a08256d0 | |
parent | 6163954e59f6a1c948d035c147ba9d18b6ca4093 (diff) | |
download | gnutls-992929a38f9c1889be1dfe9bb64bff5950291a27.tar.gz |
tests: tpm: Add a test case for tpmtool
This test case exercises tpmtool and uses certtool to create a
self-signed certificate with the TPM. It uses swtpm as TPM emulator and
configures tcsd to talk to swtpm.
Extend the Readme.md with the packages needed for TPM support and TPM test
support.
This test case needs to be run as root since tcsd needs to be started
as root.
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/scripts/common.sh | 29 | ||||
-rwxr-xr-x | tests/tpmtool_test.sh | 394 |
4 files changed, 432 insertions, 0 deletions
@@ -45,6 +45,11 @@ We require several tools to check out and build the software, including: * [bison](http://www.gnu.org/software/bison) (for datetime parser in certtool) * [libunbound](https://unbound.net/) (for DANE support) * [abi-compliance-checker](http://ispras.linuxbase.org/index.php/ABI_compliance_checker) (for make dist) +* [tcsd](http://trousers.sourceforge.net/) (for TPM support; optional) +* [swtpm](https://github.com/stefanberger/swtpm) (for TPM test; optional) +* [ncat](https://nmap.org/download.html) (for TPM test; optional) +* [tpm-tools](http://trousers.sourceforge.net/) (for TPM test; optional) +* [expect](https://core.tcl.tk/expect/index) (for TPM test; optional) The required software is typically distributed with your operating system, and the instructions for installing them differ. Here are diff --git a/tests/Makefile.am b/tests/Makefile.am index 1ccba11028..8cca4a0019 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -476,6 +476,10 @@ if ENABLE_DANE dist_check_SCRIPTS += danetool.sh endif +if ENABLE_TROUSERS +dist_check_SCRIPTS += tpmtool_test.sh +endif + else TESTS_ENVIRONMENT += WINDOWS=1 diff --git a/tests/scripts/common.sh b/tests/scripts/common.sh index 10c25738dd..94333f19b3 100644 --- a/tests/scripts/common.sh +++ b/tests/scripts/common.sh @@ -212,3 +212,32 @@ create_testdir() { trap "test -e \"$d\" && rm -rf \"$d\"" 1 15 2 echo "$d" } + +wait_for_file() { + local filename="$1" + local timeout="$2" + + local loops=$((timeout * 10)) loop=0 + + while test $loop -lt $loops; do + [ -f "$filename" ] && { + #allow file to be written to + sleep 0.2 + return 1 + } + sleep 0.1 + loop=$((loop+1)) + done + return 0 +} + +# Kill a process quietly +# @1: signal, e.g. -9 +# @2: pid +kill_quiet() { + local sig="$1" + local pid="$2" + + sh -c "kill $sig $pid &>/dev/null" + return $? +} diff --git a/tests/tpmtool_test.sh b/tests/tpmtool_test.sh new file mode 100755 index 0000000000..9f948c46ae --- /dev/null +++ b/tests/tpmtool_test.sh @@ -0,0 +1,394 @@ +#!/bin/sh + +# Copyright (C) 2018 IBM Corporation +# +# Author: Stefan Berger +# +# This file is part of GnuTLS. +# +# GnuTLS 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. +# +# GnuTLS 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 GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +srcdir="${srcdir:-.}" +CERTTOOL="${CERTTOOL:-../src/certtool${EXEEXT}}" +TPMTOOL="${TPMTOOL:-../src/tpmtool${EXEEXT}}" + +if [ "$(id -u)" -ne 0 ]; then + echo "Need to be root to run this test." + exit 77 +fi + +if [ -z "$(which swtpm 2>/dev/null)" ]; then + echo "Need swtpm package to run this test." + exit 77 +fi + +if [ -z "$(which tcsd 2>/dev/null)" ]; then + echo "Need tcsd (TrouSerS) package to run this test." + exit 77 +fi + +if [ -z "$(which tpm_createek 2>/dev/null)" ]; then + echo "Need tpm_createek from tpm-tools package to run this test." + exit 77 +fi + +if [ -z "$(which ncat 2>/dev/null)" ]; then + echo "Need ncat from nmap-ncat package to run this test." + exit 77 +fi + +if [ -z "$(which expect 2>/dev/null)" ]; then + echo "Need expect from expect package to run this test." + exit 77 +fi + +$TPMTOOL --help &>/dev/null +if [ $? -ne 0 ]; then + echo "tpmtool cannot show help screen (TPMTOOL=$TPMTOOL)." + exit 77 +fi + +$CERTTOOL --help &>/dev/null +if [ $? -ne 0 ]; then + echo "certtool cannot show help screen (CERTTOOL=$CERTTOOL)." + exit 77 +fi + +. "${srcdir}/scripts/common.sh" + +workdir=$(mktemp -d) + +SWTPM_SERVER_PORT=12345 +SWTPM_CTRL_PORT=$((SWTPM_SERVER_PORT + 1)) +SWTPM_PIDFILE=${workdir}/swtpm.pid +TCSD_LISTEN_PORT=12347 +export TSS_TCSD_PORT=$TCSD_LISTEN_PORT + +cleanup() +{ + stop_tcsd + if [ -n "$workdir" ]; then + rm -rf $workdir + fi +} + +start_swtpm() +{ + local workdir="$1" + + local res + + swtpm socket \ + --flags not-need-init \ + --pid file=$SWTPM_PIDFILE \ + --tpmstate dir=$workdir \ + --server type=tcp,port=$SWTPM_SERVER_PORT,disconnect \ + --ctrl type=tcp,port=$SWTPM_CTRL_PORT & + + if wait_for_file $SWTPM_PIDFILE 3; then + echo "Starting the swtpm failed" + return 1 + fi + + SWTPM_PID=$(cat $SWTPM_PIDFILE) + kill -0 ${SWTPM_PID} + if [ $? -ne 0 ]; then + echo "swtpm must have terminated" + return 1 + fi + + # Send TPM_Startup to TPM + res="$(/bin/echo -en '\x00\xC1\x00\x00\x00\x0C\x00\x00\x00\x99\x00\x01' | + ncat localhost ${SWTPM_SERVER_PORT} | od -tx1 -An)" + exp=' 00 c4 00 00 00 0a 00 00 00 00' + if [ "$res" != "$exp" ]; then + echo "Did not get expected response from TPM_Startup(ST_CLEAR)" + echo "expected: $exp" + echo "received: $res" + return 1 + fi + + return 0 +} + +stop_swtpm() +{ + if [ -n "$SWTPM_PID" ]; then + kill_quiet -15 $SWTPM_PID + unset SWTPM_PID + fi +} + +start_tcsd() +{ + local workdir="$1" + + local tcsd_conf=$workdir/tcsd.conf + local tcsd_system_ps_file=$workdir/system_ps_file + local tcsd_pidfile=$workdir/tcsd.pid + + start_swtpm "$workdir" + [ $? -ne 0 ] && return 1 + + cat <<_EOF_ > $tcsd_conf +port = $TCSD_LISTEN_PORT +system_ps_file = $tcsd_system_ps_file +_EOF_ + + chown tss:tss $tcsd_conf + chmod 0600 $tcsd_conf + + bash -c "TCSD_USE_TCP_DEVICE=1 TCSD_TCP_DEVICE_PORT=$SWTPM_SERVER_PORT tcsd -c $tcsd_conf -e -f &>/dev/null & echo \$! > $tcsd_pidfile; wait" & + BASH_PID=$! + + if wait_for_file $tcsd_pidfile 3; then + echo "Could not get TCSD's PID file" + return 1 + fi + + TCSD_PID=$(cat $tcsd_pidfile) + return 0 +} + +stop_tcsd() +{ + if [ -n "$TCSD_PID" ]; then + kill_quiet -15 $TCSD_PID + unset TCSD_PID + fi + stop_swtpm +} + +run_tpm_takeownership() +{ + local owner_password="$1" + local srk_password="$2" + + local prg out rc + local parm_z="" + + if [ -z "$srk_password" ]; then + parm_z="--srk-well-known" + fi + + prg="set parm_z \"$parm_z\" + spawn tpm_takeownership \$parm_z + expect { + \"Enter owner password:\" + { send \"$owner_password\n\" } + } + expect { + \"Confirm password:\" + { send \"$owner_password\n\" } + } + if { \$parm_z == \"\" } { + expect { + \"Enter SRK password:\" + { send \"$srk_password\n\" } + } + expect { + \"Confirm password:\" + { send \"$srk_password\n\" } + } + } + expect { + eof + } + catch wait result + exit [lindex \$result 3] + " + out=$(expect -c "$prg") + rc=$? + echo "$out" + return $rc +} + +setup_tcsd() +{ + local workdir="$1" + local owner_password="$2" + local srk_password="$3" + + local msg + + start_tcsd "$workdir" + [ $? -ne 0 ] && return 1 + + tpm_createek + [ $? -ne 0 ] && { + echo "Could not create EK" + return 1 + } + msg="$(run_tpm_takeownership "$owner_password" "$srk_password")" + [ $? -ne 0 ] && { + echo "Could not take ownership of TPM" + echo "$msg" + return 1 + } + return 0 +} + +run_tpmtool() +{ + local srk_password="$1" + + shift 1 + + local key_password="" # never used with --register + local prg out rc + + prg="spawn $TPMTOOL $@ + expect { + \"Enter SRK password:\" { + send \"$srk_password\n\" + exp_continue + } + \"Enter key password:\" { + send \"$key_password\n\" + exp_continue + } + \"tpmkey:\" { + exp_continue + } + eof + } + catch wait result + exit [lindex \$result 3] + " + out=$(expect -c "$prg") + rc=$? + echo "$out" + return $rc +} + +tpmtool_test() +{ + local workdir="$1" + local owner_password="$2" + local srk_password="$3" + + local params msg tpmkeyurl + local tpmpubkey=${workdir}/tpmpubkey.pem + local tpmca=${workdir}/tpmca.pem + local template=${workdir}/template + + setup_tcsd "$workdir" "$owner_password" "$srk_password" + [ $? -ne 0 ] && return 1 + + if [ -z "$srk_password" ]; then + params="--srk-well-known" + unset GNUTLS_PIN + else + export GNUTLS_PIN="$srk_password" + fi + + msg="$(run_tpmtool "${srk_password}" \ + ${params} --generate-rsa --register --signing)" + [ $? -ne 0 ] && { + echo "Could not create TPM signing key" + echo "$msg" + return 1 + } + tpmkeyurl=$(echo "${msg}" | sed -n 's/\(tpmkey:uuid=[^;]*\);.*/\1/p') + [ -z "$tpmkeyurl" ] && { + echo "Could not get TPM key URL" + return 1 + } + + msg=$(run_tpmtool "${srk_password}" \ + ${params} --test-sign ${tpmkeyurl}) + [ $? -ne 0 ] && { + echo "Could not test sign with key ${tpmkeyurl}" + echo "$msg" + return 1 + } + + msg=$(run_tpmtool "${srk_password}" \ + ${params} --pubkey=${tpmkeyurl} --outfile "${tpmpubkey}") + [ $? -ne 0 ] && { + echo "Could not get TPM key's public key" + echo "$msg" + return 1 + } + + cat <<_EOF_ >${template} +cn = test +ca +cert_signing_key +expiration_days = 1 +_EOF_ + + msg=$($CERTTOOL \ + --generate-self-signed \ + --template ${template} \ + --outfile ${tpmca} \ + --load-privkey ${tpmkeyurl} \ + --load-pubkey ${tpmpubkey} 2>&1) + [ $? -ne 0 ] && { + echo "Could not create self-signed certificate" + echo "$msg" + return 1 + } + + echo "Successfully created TPM root CA cert using key $tpmkeyurl" + + if [ -z "$($TPMTOOL --list | grep "${tpmkeyurl}")" ]; then + echo "TPM key '${tpmkeyurl}' was not found in list of TPM keys" + return 1 + fi + + msg=$(run_tpmtool "${srk_password}" \ + ${params} --delete ${tpmkeyurl}) + [ $? -ne 0 ] && { + echo "Could not delete TPM key ${tpmkeyurl}" + echo "$msg" + return 1 + } + + if [ -n "$($TPMTOOL --list | grep "${tpmkeyurl}")" ]; then + echo "TPM key '${tpmkeyurl}' was not properly deleted" + return 1 + fi + + stop_tcsd +} + +run_tests() +{ + local workdir="$1" + + [ -z "$workdir" ] && { + echo "No workdir" + return 1 + } + local srk_password + local owner_password="owner" + + for srk_password in "" "s"; do + tpmtool_test "$workdir" "$owner_password" "$srk_password" + [ $? -ne 0 ] && return 1 + stop_tcsd + rm ${workdir}/* + done + + echo "Ok" + + return 0 +} + +trap "cleanup" EXIT QUIT + +run_tests "$workdir" +exit $? |