1#!/usr/bin/env bash 2#;**********************************************************************; 3# Copyright (c) 2017 - 2018, Intel Corporation 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 9# 1. Redistributions of source code must retain the above copyright notice, 10# this list of conditions and the following disclaimer. 11# 12# 2. Redistributions in binary form must reproduce the above copyright notice, 13# this list of conditions and the following disclaimer in the documentation 14# and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26# THE POSSIBILITY OF SUCH DAMAGE. 27#;**********************************************************************; 28set -u 29 30usage_error () 31{ 32 echo "$0: $*" >&2 33 print_usage >&2 34 exit 2 35} 36print_usage () 37{ 38 cat <<END 39Usage: 40 int-log-compiler.sh TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS] 41END 42} 43while test $# -gt 0; do 44 case $1 in 45 --help) print_usage; exit $?;; 46 --) shift; break;; 47 -*) usage_error "invalid option: '$1'";; 48 *) break;; 49 esac 50 shift 51done 52 53# Verify the running shell and OS environment is sufficient to run these tests. 54sanity_test () 55{ 56 # Check special file 57 if [ ! -e /dev/urandom ]; then 58 echo "Missing file /dev/urandom; exiting" 59 exit 1 60 fi 61 62 # Check ps 63 PS_LINES=$(ps -e 2>/dev/null | wc -l) 64 if [ "$PS_LINES" -eq 0 ] ; then 65 echo "Command ps not listing processes; exiting" 66 exit 1 67 fi 68 69 if [ -z "$(which tpm_server)" ]; then 70 echo "tpm_server not on PATH; exiting" 71 exit 1 72 fi 73 74 if [ -z "$(which ss)" ]; then 75 echo "ss not on PATH; exiting" 76 exit 1 77 fi 78} 79 80# This function takes a PID as a parameter and determines whether or not the 81# process is currently running. If the daemon is running 0 is returned. Any 82# other value indicates that the daemon isn't running. 83daemon_status () 84{ 85 local pid=$1 86 87 if [ $(kill -0 "${pid}" 2> /dev/null) ]; then 88 echo "failed to detect running daemon with PID: ${pid}"; 89 return 1 90 fi 91 return 0 92} 93 94# This is a generic function to start a daemon, setup the environment 95# variables, redirect output to a log file, store the PID of the daemon 96# in a file and disconnect the daemon from the parent shell. 97daemon_start () 98{ 99 local daemon_bin="$1" 100 local daemon_opts="$2" 101 local daemon_log_file="$3" 102 local daemon_pid_file="$4" 103 local daemon_env="$5" 104 105 env ${daemon_env} stdbuf -o0 -e0 ${daemon_bin} ${daemon_opts} > ${daemon_log_file} 2>&1 & 106 local ret=$? 107 local pid=$! 108 if [ ${ret} -ne 0 ]; then 109 echo "failed to start daemon: \"${daemon_bin}\" with env: \"${daemon_env}\"" 110 exit ${ret} 111 fi 112 sleep 1 113 daemon_status "${pid}" 114 if [ $? -ne 0 ]; then 115 echo "daemon died after successfully starting in background, check " \ 116 "log file: ${daemon_log_file}" 117 return 1 118 fi 119 echo ${pid} > ${daemon_pid_file} 120 disown ${pid} 121 echo "successfully started daemon: ${daemon_bin} with PID: ${pid}" 122 return 0 123} 124# function to start the simulator 125# This also that we have a private place to store the NVChip file. Since we 126# can't tell the simulator what to name this file we must generate a random 127# directory under /tmp, move to this directory, start the simulator, then 128# return to the old pwd. 129simulator_start () 130{ 131 local sim_bin="$1" 132 local sim_port="$2" 133 local sim_log_file="$3" 134 local sim_pid_file="$4" 135 local sim_tmp_dir="$5" 136 # simulator port is a random port between 1024 and 65535 137 138 cd ${sim_tmp_dir} 139 daemon_start "${sim_bin}" "-port ${sim_port}" "${sim_log_file}" \ 140 "${sim_pid_file}" "" 141 local ret=$? 142 cd - 143 return $ret 144} 145# function to stop a running daemon 146# This function takes a single parameter: a file containing the PID of the 147# process to be killed. The PID is extracted and the daemon killed. 148daemon_stop () 149{ 150 local pid_file=$1 151 local pid=0 152 local ret=0 153 154 if [ ! -f ${pid_file} ]; then 155 echo "failed to stop daemon, no pid file: ${pid_file}" 156 return 1 157 fi 158 pid=$(cat ${pid_file}) 159 daemon_status "${pid}" 160 ret=$? 161 if [ ${ret} -ne 0 ]; then 162 echo "failed to detect running daemon with PID: ${pid}"; 163 return ${ret} 164 fi 165 kill ${pid} 166 ret=$? 167 if [ ${ret} -ne 0 ]; then 168 echo "failed to kill daemon process with PID: ${pid}" 169 fi 170 return ${ret} 171} 172 173OS=$(uname) 174 175if [ "$OS" == "Linux" ]; then 176 sanity_test 177fi 178 179# Once option processing is done, $@ should be the name of the test executable 180# followed by all of the options passed to the test executable. 181TEST_BIN=$(realpath "$1") 182TEST_DIR=$(dirname "$1") 183TEST_NAME=$(basename "${TEST_BIN}") 184 185# start an instance of the simulator for the test, have it use a random port 186SIM_LOG_FILE=${TEST_BIN}_simulator.log 187SIM_PID_FILE=${TEST_BIN}_simulator.pid 188SIM_TMP_DIR=$(mktemp -d /tmp/tpm_server_XXXXXX) 189PORT_MIN=1024 190PORT_MAX=65534 191BACKOFF_FACTOR=2 192BACKOFF_MAX=6 193BACKOFF=1 194 195sock_tool="unknown" 196 197if [ "$OS" == "Linux" ]; then 198 sock_tool="ss -lntp4" 199elif [ "$OS" == "FreeBSD" ]; then 200 sock_tool="sockstat -l4" 201fi 202 203for i in $(seq ${BACKOFF_MAX}); do 204 SIM_PORT_DATA=$(od -A n -N 2 -t u2 /dev/urandom | awk -v min=${PORT_MIN} -v max=${PORT_MAX} '{print ($1 % (max - min)) + min}') 205 if [ $(expr ${SIM_PORT_DATA} % 2) -eq 1 ]; then 206 SIM_PORT_DATA=$((${SIM_PORT_DATA}-1)) 207 fi 208 SIM_PORT_CMD=$((${SIM_PORT_DATA}+1)) 209 echo "Starting simulator on port ${SIM_PORT_DATA}" 210 simulator_start tpm_server ${SIM_PORT_DATA} ${SIM_LOG_FILE} ${SIM_PID_FILE} ${SIM_TMP_DIR} 211 sleep 1 # give daemon time to bind to ports 212 if [ ! -s ${SIM_PID_FILE} ] ; then 213 echo "Simulator PID file is empty or missing. Giving up." 214 exit 1 215 fi 216 PID=$(cat ${SIM_PID_FILE}) 217 echo "simulator PID: ${PID}"; 218 ${sock_tool} 2> /dev/null | grep "${PID}" | grep "${SIM_PORT_DATA}" 219 ret_data=$? 220 ${sock_tool} 2> /dev/null | grep "${PID}" | grep "${SIM_PORT_CMD}" 221 ret_cmd=$? 222 if [ \( $ret_data -eq 0 \) -a \( $ret_cmd -eq 0 \) ]; then 223 echo "Simulator with PID ${PID} bound to port ${SIM_PORT_DATA} and " \ 224 "${SIM_PORT_CMD} successfully."; 225 break 226 fi 227 echo "Port conflict? Cleaning up PID: ${PID}" 228 kill "${PID}" 229 BACKOFF=$((${BACKOFF}*${BACKOFF_FACTOR})) 230 echo "Failed to start simulator: port ${SIM_PORT_DATA} or " \ 231 "${SIM_PORT_CMD} probably in use. Retrying in ${BACKOFF}." 232 sleep ${BACKOFF} 233 if [ $i -eq 10 ]; then 234 echo "Failed to start simulator after $i tries. Giving up."; 235 exit 1 236 fi 237done 238 239while true; do 240 241env TPM20TEST_TCTI_NAME="socket" \ 242 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 243 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 244 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 245 G_MESSAGES_DEBUG=all ./test/helper/tpm_startup 246if [ $? -ne 0 ]; then 247 echo "TPM_StartUp failed" 248 ret=99 249 break 250fi 251 252EKPUB_FILE=${TEST_BIN}_ekpub.pem 253EKCERT_FILE=${TEST_BIN}_ekcert.crt 254EKCERT_PEM_FILE=${TEST_BIN}_ekcert.pem 255 256 257env TPM20TEST_TCTI_NAME="socket" \ 258 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 259 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 260 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 261 G_MESSAGES_DEBUG=all ./test/helper/tpm_getek>$EKPUB_FILE 262if [ $? -ne 0 ]; then 263 echo "TPM_getek failed" 264 ret=99 265 break 266fi 267 268EKECCPUB_FILE=${TEST_BIN}_ekeccpub.pem 269EKECCCERT_FILE=${TEST_BIN}_ekecccert.crt 270EKECCCERT_PEM_FILE=${TEST_BIN}_ekecccert.pem 271 272env TPM20TEST_TCTI_NAME="socket" \ 273 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 274 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 275 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 276 G_MESSAGES_DEBUG=all ./test/helper/tpm_getek_ecc>$EKECCPUB_FILE 277if [ $? -ne 0 ]; then 278 echo "TPM_getek_ecc failed" 279 ret=99 280 break 281fi 282 283INTERMEDCA_FILE=${TEST_BIN}_intermedecc-ca 284ROOTCA_FILE=${TEST_BIN}_root-ca 285 286if [ "$OS" == "Linux" ]; then 287 SCRIPTDIR="$(dirname $(realpath $0))/" 288 ${SCRIPTDIR}/ekca/create_ca.sh "${EKPUB_FILE}" "${EKECCPUB_FILE}" "${EKCERT_FILE}" \ 289 "${EKECCCERT_FILE}" "${INTERMEDCA_FILE}" "${ROOTCA_FILE}" >${TEST_BIN}_ca.log 2>&1 290 if [ $? -ne 0 ]; then 291 echo "ek-cert ca failed" 292 ret=99 293 break 294 fi 295fi 296 297# Determine the fingerprint of the RSA EK public. 298FINGERPRINT=$(openssl pkey -pubin -inform PEM -in $EKPUB_FILE -outform DER | sha256sum | cut -f 1 -d ' ') 299export FAPI_TEST_FINGERPRINT=" { \"hashAlg\" : \"sha256\", \"digest\" : \"$FINGERPRINT\" }" 300openssl x509 -inform DER -in $EKCERT_FILE -outform PEM -out $EKCERT_PEM_FILE 301export FAPI_TEST_CERTIFICATE="file:${EKCERT_PEM_FILE}" 302 303# Determine the fingerprint of the RSA EK public. 304FINGERPRINT_ECC=$(openssl pkey -pubin -inform PEM -in $EKECCPUB_FILE -outform DER | sha256sum | cut -f 1 -d ' ') 305export FAPI_TEST_FINGERPRINT_ECC=" { \"hashAlg\" : \"sha256\", \"digest\" : \"$FINGERPRINT_ECC\" }" 306openssl x509 -inform DER -in $EKECCCERT_FILE -outform PEM -out $EKECCCERT_PEM_FILE 307export FAPI_TEST_CERTIFICATE_ECC="file:${EKECCCERT_PEM_FILE}" 308 309cat $EKCERT_FILE | \ 310env TPM20TEST_TCTI_NAME="socket" \ 311 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 312 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 313 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 314 G_MESSAGES_DEBUG=all ./test/helper/tpm_writeekcert 1C00002 315if [ $? -ne 0 ]; then 316 echo "TPM_writeekcert failed" 317 ret=99 318 break 319fi 320 321cat $EKECCCERT_FILE | \ 322env TPM20TEST_TCTI_NAME="socket" \ 323 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 324 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 325 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 326 G_MESSAGES_DEBUG=all ./test/helper/tpm_writeekcert 1C0000A 327if [ $? -ne 0 ]; then 328 echo "TPM_writeekcert failed" 329 ret=99 330fi 331 332env TPM20TEST_TCTI_NAME="socket" \ 333 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 334 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 335 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 336 G_MESSAGES_DEBUG=all ./test/helper/tpm_transientempty 337if [ $? -ne 0 ]; then 338 echo "TPM transient area not empty => skipping" 339 ret=99 340 break 341fi 342 343TPMSTATE_FILE1=${TEST_BIN}_state1 344TPMSTATE_FILE2=${TEST_BIN}_state2 345 346env TPM20TEST_TCTI_NAME="socket" \ 347 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 348 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 349 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 350 G_MESSAGES_DEBUG=all ./test/helper/tpm_dumpstate>$TPMSTATE_FILE1 351if [ $? -ne 0 ]; then 352 echo "Error during dumpstate" 353 ret=99 354 break 355fi 356 357echo "Execute the test script" 358env TPM20TEST_TCTI_NAME="socket" \ 359 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 360 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 361 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 362 FAPI_TEST_ROOT_CERT=${ROOTCA_FILE}.pem \ 363 G_MESSAGES_DEBUG=all $@ 364ret=$? 365echo "Script returned $ret" 366 367#We check the state before a reboot to see if transients and NV were chagned. 368env TPM20TEST_TCTI_NAME="socket" \ 369 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 370 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 371 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 372 G_MESSAGES_DEBUG=all ./test/helper/tpm_dumpstate>$TPMSTATE_FILE2 373if [ $? -ne 0 ]; then 374 echo "Error during dumpstate" 375 ret=99 376 break 377fi 378 379if [ "$(cat $TPMSTATE_FILE1)" != "$(cat $TPMSTATE_FILE2)" ]; then 380 echo "TPM changed state during test" 381 echo "State before ($TPMSTATE_FILE1):" 382 cat $TPMSTATE_FILE1 383 echo "State after ($TPMSTATE_FILE2):" 384 cat $TPMSTATE_FILE2 385 ret=1 386 break 387fi 388 389break 390 391#TODO: Add a tpm-restart/reboot here 392 393#We check the state again after a reboot to see if PCR allocations were chagned. 394env TPM20TEST_TCTI_NAME="socket" \ 395 TPM20TEST_SOCKET_ADDRESS="127.0.0.1" \ 396 TPM20TEST_SOCKET_PORT="${SIM_PORT_DATA}" \ 397 TPM20TEST_TCTI="mssim:host=127.0.0.1,port=${SIM_PORT_DATA}" \ 398 G_MESSAGES_DEBUG=all ./test/helper/tpm_dumpstate>$TPMSTATE_FILE2 399if [ $? -ne 0 ]; then 400 echo "Error during dumpstate" 401 ret=99 402 break 403fi 404 405if [ "$(cat $TPMSTATE_FILE1)" != "$(cat $TPMSTATE_FILE2)" ]; then 406 echo "TPM changed state during test" 407 echo "State before ($TPMSTATE_FILE1):" 408 cat $TPMSTATE_FILE1 409 echo "State after ($TPMSTATE_FILE2):" 410 cat $TPMSTATE_FILE2 411 ret=1 412 break 413fi 414 415break 416done 417 418# This sleep is sadly necessary: If we kill the tabrmd w/o sleeping for a 419# second after the test finishes the simulator will die too. Bug in the 420# simulator? 421sleep 1 422# teardown 423daemon_stop ${SIM_PID_FILE} 424rm -rf ${SIM_TMP_DIR} ${SIM_PID_FILE} 425 426exit $ret 427