swtpm/tests/common
Stefan Berger 3928bd2c1c tests: Use nm and grep to check for ASAN
clang doesn't link executables built with ASAN support to libasan, like
gcc does, so we have to use nm rather than ldd for checking for whether
the executable was built with ASAN. nm is part of the binutils package
and should be available on all systems where gcc was installed.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2021-09-29 14:57:38 -04:00

904 lines
18 KiB
Plaintext

SWTPM=swtpm
SWTPM_EXE=${SWTPM_EXE:-${ROOT}/src/swtpm/${SWTPM}}
SWTPM_IOCTL=${SWTPM_IOCTL:-${ROOT}/src/swtpm_ioctl/swtpm_ioctl}
SWTPM_BIOS=${SWTPM_BIOS:-${ROOT}/src/swtpm_bios/swtpm_bios}
SWTPM_SETUP=${SWTPM_SETUP:-${ROOT}/src/swtpm_setup/swtpm_setup}
SWTPM_CERT=${SWTPM_CERT:-${ROOT}/src/swtpm_cert/swtpm_cert}
ECHO=$(type -P echo)
case "$(uname -s)" in
Darwin)
CERTTOOL=gnutls-certtool;;
*)
CERTTOOL=certtool;;
esac
# Note: Do not use file descriptors above 127 due to OpenBSD.
# Kill a process quietly
# @1: signal, e.g. -9
# @2: pid
function kill_quiet()
{
local sig="$1"
local pid="$2"
bash -c "kill $sig $pid &>/dev/null"
return $?
}
# Wait for a regular file to appear and for it to have > 0 bytes
#
# @1: filename
# @2: timeout in seconds
function wait_for_file()
{
local filename="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
[ -f "${filename}" ] && [ $(get_filesize ${filename}) != 0 ] && {
return 1
}
sleep 0.1
done
return 0
}
# Wait for a regular file to disappear
#
# @1: filename
# @2: timeout in seconds
function wait_file_gone()
{
local filename="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
[ -f "${filename}" ] || return 1
sleep 0.1
done
return 0
}
# Wait for a process with given PID to be gone
#
# @1: pid
# @2: timeout in seconds
function wait_process_gone()
{
local pid="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
kill_quiet -0 ${pid} || return 1
sleep 0.1
done
return 0
}
# Wait for a chardev to appear
#
# @1: filename
# @2: timeout in seconds
function wait_for_chardev()
{
local filename="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
[ -c "${filename}" ] && return 1
sleep 0.1
done
return 0
}
# Wait for a chardev to disappear
#
# @1: filename
# @2: timeout in seconds
function wait_chardev_gone()
{
local filename="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
[ -c "${filename}" ] || return 1
sleep 0.1
done
return 0
}
# Wait for a socket file to appear
#
# @1: filename
# @2: timeout in seconds
function wait_for_socketfile()
{
local filename="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
[ -S "${filename}" ] && return 1
sleep 0.1
done
return 0
}
# Wait for a socket file to disappear
#
# @1: filename
# @2: timeout in seconds
function wait_socketfile_gone()
{
local filename="$1"
local timeout="$2"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
[ -S "${filename}" ] || return 1
sleep 0.1
done
return 0
}
# Wait for a server socket to appear
#
# @1: port
# @2: host
# @3: timeout in seconds
function wait_for_serversocket()
{
local port="$1"
local host="$2"
local timeout="$3"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
(exec 127<>/dev/tcp/${host}/${port}) &>/dev/null
[ $? -eq 0 ] && return 1
sleep 0.1
done
return 0
}
# Wait for a server socket to disappear
#
# @1: port
# @2: host
# @3: timeout in seconds
function wait_serversocket_gone()
{
local port="$1"
local host="$2"
local timeout="$3"
local loops=$((timeout * 10)) loop
for ((loop=0; loop<loops; loop++)); do
(exec 127<>/dev/tcp/${host}/${port}) &>/dev/null
[ $? -eq 0 ] || return 1
sleep 0.1
done
return 0
}
# Wait for a TCP port to open for listening
# @1: port
# @2: id of process to open port
# @3: timeout in seconds
function wait_port_open()
{
local port=$1
local pid=$2
local timeout=$3
local loops=$((timeout * 10)) loop
local NETSTAT=$(type -P netstat)
for ((loop = 0; loop < loops; loop++)); do
if [ -n "$NETSTAT" ]; then
if [ -n "$(netstat -naptl 2>/dev/null |
grep "LISTEN" |
grep " $pid/" |
grep ":$port ")" ]; then
return 1
fi
else
if [ -n "$(ss -nptl |
grep ",pid=${pid}," |
grep ":$port ")" ]; then
return 1
fi
fi
sleep 0.1
done
return 0
}
# Wait for a TCP listening port to close
# @1: port
# @2: id of process to close port
# @3: timeout in seconds
function wait_port_closed()
{
local port=$1
local pid=$2
local timeout=$3
local loops=$((timeout * 10)) loop
local NETSTAT=$(type -P netstat)
for ((loop = 0; loop < loops; loop++)); do
if [ -n "$NETSTAT" ]; then
if [ -z "$(netstat -naptl 2>/dev/null |
grep "LISTEN" |
grep " $pid/" |
grep ":$port ")" ]; then
return 1
fi
else
if [ -z "$(ss -nptl |
grep ",pid=${pid}," |
grep ":$port ")" ]; then
return 1
fi
fi
sleep 0.1
done
return 0
}
# Run the swtpm_ioctl command
#
# @param1: type of interface
function run_swtpm_ioctl()
{
local iface=$1; shift
case "${iface}" in
cuse)
[ -z "${SWTPM_DEV_NAME}" ] && {
echo "SWTPM_DEV_NAME not defined"
exit 1
}
${SWTPM_IOCTL} $@ ${SWTPM_DEV_NAME}
return $?
;;
socket+socket|unix+socket)
[ -z "${SWTPM_SERVER_NAME}" ] && {
echo "SWTPM_SERVER_NAME not defined"
exit 1
}
[ -z "${SWTPM_SERVER_PORT}" ] && {
echo "SWTPM_SERVER_PORT not defined"
exit 1
}
${SWTPM_IOCTL} \
--tcp ${SWTPM_SERVER_NAME}:${SWTPM_CTRL_PORT} \
$@
return $?
;;
socket+unix|unix+unix)
[ -z "${SWTPM_CTRL_UNIX_PATH}" ] && {
echo "SWTPM_CTRL_UNIX_PATH not defined"
exit 1
}
${SWTPM_IOCTL} \
--unix ${SWTPM_CTRL_UNIX_PATH} \
$@
return $?
;;
esac
}
# Start the swtpm in the background
#
# @param1: type of interface
# @param2.. : parameters to pass to 'swtpm'
function run_swtpm()
{
local iface=$1; shift
local swtpm_server_disconnect=""
echo "==== Starting swtpm with interfaces ${iface} ===="
if [ -z "${SWTPM_SERVER_NO_DISCONNECT}" ]; then
swtpm_server_disconnect=",disconnect"
fi
case "${iface}" in
cuse)
[ -z "${SWTPM_DEV_NAME}" ] && {
echo "SWTPM_DEV_NAME not defined"
exit 1
}
if wait_chardev_gone ${SWTPM_DEV_NAME} 2; then
echo "${SWTPM_DEV_NAME} is still there and may be used."
exit 1
fi
${SWTPM_EXE} cuse "$@" ${SWTPM_TEST_SECCOMP_OPT} \
-n ${SWTPM_DEV_NAME##*/}
rc=$?
if [ $rc -ne 0 ]; then
echo "Could not run ${SWTPM_EXE} using ${iface}"
exit 1
fi
if wait_for_chardev ${SWTPM_DEV_NAME} 2; then
echo "$SWTPM_DEV_NAME did not appear"
exit 1
fi
SWTPM_PID=$(ps aux |
grep "cuse" |
grep -E " ${SWTPM_DEV_NAME##*/}\$" |
grep -v grep |
gawk '{print $2}')
return $?
;;
socket+socket)
[ -z "${SWTPM_SERVER_PORT}" ] && {
echo "SWTPM_SERVER_PORT not defined"
exit 1
}
[ -z "${SWTPM_CTRL_PORT}" ] && {
echo "SWTPM_CTRL_PORT not defined"
exit 1
}
if wait_serversocket_gone "${SWTPM_SERVER_PORT}" 127.0.0.1 2; then
echo "Port ${SWTPM_SERVER_PORT} is still used"
exit 1
fi
if wait_serversocket_gone "${SWTPM_CTRL_PORT}" 127.0.0.1 1; then
echo "Port ${SWTPM_CTRL_PORT} is still used"
exit 1
fi
${SWTPM_EXE} socket "$@" \
${SWTPM_TEST_SECCOMP_OPT} \
--server type=tcp,port=${SWTPM_SERVER_PORT}${swtpm_server_disconnect} \
--ctrl type=tcp,port=${SWTPM_CTRL_PORT} &
rc=$?
if [ $rc -ne 0 ]; then
echo "Could not run ${SWTPM_EXE} using ${iface}"
exit 1
fi
SWTPM_PID=$!
if wait_for_serversocket "${SWTPM_SERVER_PORT}" 127.0.0.1 2; then
echo "Server did not open port ${SWTPM_SERVER_PORT}"
kill -9 ${SWTPM_PID}
exit 1
fi
if wait_for_serversocket "${SWTPM_CTRL_PORT}" 127.0.0.1 1; then
echo "Server did not open port ${SWTPM_CTRL_PORT}"
kill -9 ${SWTPM_PID}
exit 1
fi
return 0
;;
socket+unix)
[ -z "${SWTPM_SERVER_PORT}" ] && {
echo "SWTPM_SERVER_PORT not defined"
exit 1
}
[ -z "${SWTPM_CTRL_UNIX_PATH}" ] && {
echo "SWTPM_CTRL_UNIX_PATH not defined"
exit 1
}
if wait_serversocket_gone "${SWTPM_SERVER_PORT}" 127.0.0.1 2; then
echo "Port ${SWTPM_SERVER_PORT} is still used"
exit 1
fi
if wait_socketfile_gone "${SWTPM_CTRL_UNIX_PATH}" 2; then
echo "Unix socket ${SWTPM_CTRL_UNIX_PATH} is still there"
exit 1
fi
${SWTPM_EXE} socket "$@" \
${SWTPM_TEST_SECCOMP_OPT} \
--server type=tcp,port=${SWTPM_SERVER_PORT}${swtpm_server_disconnect} \
--ctrl type=unixio,path=${SWTPM_CTRL_UNIX_PATH} &
rc=$?
if [ $rc -ne 0 ]; then
echo "Could not run ${SWTPM_EXE} using ${iface}"
exit 1
fi
[ $rc -ne 0 ] && return $rc
SWTPM_PID=$!
if wait_for_serversocket "${SWTPM_SERVER_PORT}" 127.0.0.1 2; then
echo "Server did not open port ${SWTPM_SERVER_PORT}"
kill -9 ${SWTPM_PID}
exit 1
fi
if wait_for_socketfile ${SWTPM_CTRL_UNIX_PATH} 1; then
echo "Server did not create UnixIO socket ${SWTPM_CTRL_UNIX_PATH}"
kill -9 ${SWTPM_PID}
exit 1
fi
return 0
;;
unix+socket)
[ -z "${SWTPM_CMD_UNIX_PATH}" ] && {
echo "SWTPM_CMD_UNIX_PATH not defined"
exit 1
}
[ -z "${SWTPM_CTRL_PORT}" ] && {
echo "SWTPM_CTRL_PORT not defined"
exit 1
}
if wait_socketfile_gone "${SWTPM_CMD_UNIX_PATH}" 2; then
echo "Unix socket ${SWTPM_CMD_UNIX_PATH} is still there"
exit 1
fi
if wait_serversocket_gone "${SWTPM_CTRL_PORT}" 127.0.0.1 1; then
echo "Port ${SWTPM_CTRL_PORT} is still used"
exit 1
fi
${SWTPM_EXE} socket "$@" \
${SWTPM_TEST_SECCOMP_OPT} \
--server type=unixio,path=${SWTPM_CMD_UNIX_PATH} \
--ctrl type=tcp,port=${SWTPM_CTRL_PORT} &
rc=$?
if [ $rc -ne 0 ]; then
echo "Could not run ${SWTPM_EXE} using ${iface}"
exit 1
fi
SWTPM_PID=$!
if wait_for_socketfile ${SWTPM_CMD_UNIX_PATH} 2; then
echo "Server did not create UnixIO socket ${SWTPM_CMD_UNIX_PATH}"
kill -9 ${SWTPM_PID}
exit 1
fi
if wait_for_serversocket "${SWTPM_CTRL_PORT}" 127.0.0.1 1; then
echo "Server did not open port ${SWTPM_CTRL_PORT}"
kill -9 ${SWTPM_PID}
exit 1
fi
return 0
;;
unix+unix)
[ -z "${SWTPM_CMD_UNIX_PATH}" ] && {
echo "SWTPM_CMD_UNIX_PATH not defined"
exit 1
}
[ -z "${SWTPM_CTRL_UNIX_PATH}" ] && {
echo "SWTPM_CTRL_UNIX_PATH not defined"
exit 1
}
if wait_socketfile_gone "${SWTPM_CMD_UNIX_PATH}" 2; then
echo "Unix socket ${SWTPM_CMD_UNIX_PATH} is still there"
exit 1
fi
if wait_socketfile_gone "${SWTPM_CTRL_UNIX_PATH}" 2; then
echo "Unix socket ${SWTPM_CTRL_UNIX_PATH} is still there"
exit 1
fi
${SWTPM_EXE} socket "$@" \
${SWTPM_TEST_SECCOMP_OPT} \
--server type=unixio,path=${SWTPM_CMD_UNIX_PATH} \
--ctrl type=unixio,path=${SWTPM_CTRL_UNIX_PATH} &
rc=$?
if [ $rc -ne 0 ]; then
echo "Could not run ${SWTPM_EXE} using ${iface}"
exit 1
fi
SWTPM_PID=$!
if wait_for_socketfile ${SWTPM_CMD_UNIX_PATH} 2; then
echo "Server did not create UnixIO socket ${SWTPM_CMD_UNIX_PATH}"
kill -9 ${SWTPM_PID}
exit 1
fi
if wait_for_socketfile ${SWTPM_CTRL_UNIX_PATH} 1; then
echo "Server did not create UnixIO socket ${SWTPM_CTRL_UNIX_PATH}"
kill -9 ${SWTPM_PID}
exit 1
fi
return 0
;;
esac
}
# Open the command channel/device on fd 100
#
# @param1: type of interface
# @param2: must be '100'
function swtpm_open_cmddev()
{
local iface=$1; shift
[ "$1" != "100" ] && {
echo "swtpm_opendev: Filedescriptor must be 100"
exit 1
}
case "${iface}" in
cuse)
[ -z "${SWTPM_DEV_NAME}" ] && {
echo "SWTPM_DEV_NAME not defined"
exit 1
}
exec 100<>${SWTPM_DEV_NAME}
return $?
;;
socket+socket|socket+unix)
[ -z "${SWTPM_SERVER_NAME}" ] && {
echo "SWTPM_SERVER_NAME not defined"
exit 1
}
[ -z "${SWTPM_SERVER_PORT}" ] && {
echo "SWTPM_SERVER_PORT not defined"
exit 1
}
# Must first close on OS/X
exec 100>&-
exec 100<>/dev/tcp/${SWTPM_SERVER_NAME}/${SWTPM_SERVER_PORT}
return $?
;;
unix+socket|unix+unix)
;;
*)
echo "swtpm_opendev: unsupported interface $iface"
exit 1
esac
}
# Transmit a command on fd 100
#
# @param1: type of interface
function swtpm_cmd_tx()
{
local iface=$1
local cmd_path resp_path
cmd_path=$(mktemp)
case "${iface}" in
cuse)
echo -en "$2" > ${cmd_path}
cat ${cmd_path} >&100
dd if=/proc/self/fd/100 2>/dev/null | \
od -t x1 -A n | \
tr -s ' ' | \
tr -d '\n' | \
sed 's/ $//g'
;;
socket+socket|socket+unix)
echo -en "$2" > ${cmd_path}
cat ${cmd_path} >&100
cat <&100 | od -t x1 -A n | \
tr -s ' ' | \
tr -d '\n' | \
sed 's/ $//g'
;;
unix+socket|unix+unix)
echo -en "$2" > ${cmd_path}
socat -x -t50 \
FILE:${cmd_path},rdonly \
UNIX-CLIENT:${SWTPM_CMD_UNIX_PATH} 2>&1 | \
sed -n '/^ /p' | \
tail -n1
;;
*)
echo "swtpm_opendev: unsupported interface $iface"
rm -f ${cmd_path}
exit 1
esac
rm -f ${cmd_path}
}
# Transmit a control command on fd 101
#
# @param1: type of interface
function swtpm_ctrl_tx()
{
local iface=$1
local ctrl_path resp_path
case "${iface}" in
socket+socket|unix+socket)
$ECHO -en "$2" >&101
cat <&101 | od -t x1 -A n -w128
;;
socket+unix|unix+unix)
ctrl_path=$(mktemp)
echo -en "$2" > ${ctrl_path}
socat -x -t50 \
FILE:${ctrl_path},rdonly \
UNIX-CLIENT:${SWTPM_CTRL_UNIX_PATH} 2>&1 | \
sed -n '/^ /p' | \
tail -n1
rm -f ${ctrl_path}
;;
*)
echo "swtpm_opendev: unsupported interface $iface"
exit 1
esac
}
# Run swtpm_bios
#
# @param1: type of interface
# @param2 ...: parameters to pass to swtpm_bios
function run_swtpm_bios()
{
local iface=$1
shift
case "${iface}" in
cuse)
[ -z "${SWTPM_DEV_NAME}" ] && {
echo "SWTPM_DEV_NAME not defined"
exit 1
}
${SWTPM_BIOS} --tpm-device ${SWTPM_DEV_NAME} $@
return $?
;;
unix+unix|unix+socket)
[ -z "${SWTPM_CMD_UNIX_PATH}" ] && {
echo "SWTPM_CMD_UNIX_PATH not defined"
exit 1
}
${SWTPM_BIOS} --unix ${SWTPM_CMD_UNIX_PATH} $@
return $?
;;
socket+unix|socket+socket)
[ -z "${SWTPM_SERVER_PORT}" ] && {
echo "SWTPM_SERVER_PORT not defined"
exit 1
}
${SWTPM_BIOS} --tcp ${SWTPM_SERVER_NAME}:${SWTPM_SERVER_PORT} $@
return $?
;;
*)
echo "run_swtpm_bios: unsupported interface $iface"
exit 1
esac
}
# Get the size of a file in bytes
#
# @1: filename
function get_filesize()
{
if [[ "$(uname -s)" =~ (Linux|CYGWIN_NT-) ]]; then
stat -c%s $1
else
# OpenBSD
stat -f%z $1
fi
}
# Get the file mode bits in octal format
#
# @1: filename
function get_filemode()
{
if [[ "$(uname -s)" =~ (Linux|CYGWIN_NT-) ]]; then
stat -c%a $1
else
# BSDs
stat -f%Lp $1
fi
}
# Get the file owner uid and gid
#
# @1: filename
function get_fileowner()
{
if [[ "$(uname -s)" =~ (Linux|CYGWIN_NT-) ]]; then
stat -c"%u %g" $1
else
# BSDs
stat -f"%u %g" $1
fi
}
# Get the file owner user name and group name
#
# @1: filename
function get_fileowner_names()
{
if [[ "$(uname -s)" =~ (Linux|CYGWIN_NT-) ]]; then
stat -c"%U %G" $1
else
# BSDs
stat -f"%Su %Sg" $1
fi
}
# Get the SHA1 of a file
#
# @1: filename
function get_sha1_file()
{
if ! [ -r $1 ]; then
echo "[file $1 does not exist]"
return
fi
case "$(uname -s)" in
Linux|CYGWIN*)
sha1sum $1 | cut -f1 -d" "
;;
Darwin)
shasum $1 | cut -f1 -d" "
;;
*)
# OpenBSD
sha1 $1 | cut -d "=" -f2 | tr -d " "
esac
}
# Display process that have the same name
#
# @1: process name to match
function display_processes_by_name()
{
local name="$1"
if [ 1 -eq 0 ]; then
ps aux | grep "${name}" | grep -v grep
fi
}
# Check whether seccomp support is compiled in
#
# @1: path to swtpm
#
# Returns 0 if seccomp is supported, 1 otherwise
function has_seccomp_support()
{
local swtpm_exe="$1"
local tmp=$(${swtpm_exe} socket --help | grep -E "\-\-seccomp")
[ -n "${tmp}" ] && return 0
return 1
}
# Check whether the given process runs with the given seccomp
# profile type IF the given swtpm executable has seccomp support
#
# @1: Path to swtpm executable from which process was started
# @2: The process ID
# @3: The expected seccomp profile type
function check_seccomp_profile()
{
local swtpm_exe="$1"
local swtpm_pid="$2"
local profile="$3"
local tmp
if ! has_seccomp_support "${swtpm_exe}"; then
return 0
fi
if [ -n "${SWTPM_TEST_SECCOMP_OPT}" ]; then
return 0
fi
tmp=$(grep -E "^Seccomp" /proc/self/status |
cut -d":" -f2 |
tr -d '\t')
if [ "${tmp}" != "0" ]; then
echo "check_seccomp_profile: skipping check since test env." \
"runs with in a seccomp profile overriding --seccomp"
return 0
fi
tmp=$(grep -E "^Seccomp" /proc/${swtpm_pid}/status |
cut -d":" -f2 |
tr -d '\t')
if [ "${tmp}" != ${profile} ]; then
echo "Process ${swtpm_pid} has wrong seccomp profile type"
echo "Expected: ${profile}"
echo "Actual : ${tmp}"
return 1
fi
return 0
}
# Validate the content of the pid file
# @1: Expected PID
# @2: pid file filename
function validate_pidfile()
{
local pid="$1"
local pidfile="$2"
local rpid="$(cat $pidfile)"
if [ -z "$rpid" ]; then
sleep 0.1
rpid="$(cat $pidfile)"
fi
if [ "$pid" != "$rpid" ]; then
echo "Error: pid file contains unexpected PID value."
echo "expected: $pid"
echo "actual : $(cat $pidfile)"
exit 1
fi
}
# Check whether swtpm can use a TPM 1.2
function skip_test_no_tpm12()
{
local swtpm_exe="$1"
local res=$(${swtpm_exe} socket --print-capabilities | grep '"tpm-1.2"')
if [ -z "${res}" ]; then
echo "${swtpm_exe} does not provide a TPM 1.2"
exit 77
fi
}
# Check whether swtpm can use a TPM 2.0
function skip_test_no_tpm20()
{
local swtpm_exe="$1"
local res=$(${swtpm_exe} socket --print-capabilities | grep '"tpm-2.0"')
if [ -z "${res}" ]; then
echo "${swtpm_exe} does not provide a TPM 2.0"
exit 77
fi
}
# Check whether swtpm supports chardev interface
function skip_test_no_chardev()
{
local swtpm_exe="$1"
local res=$(${swtpm_exe} chardev --help 2>&1 |
grep "Unsupported TPM interface")
if [ -n "${res}" ]; then
echo "${swtpm_exe} does not support chardev interface"
exit 77
fi
}
# Check whether swtpm links with ASAN
function skip_test_linked_with_asan()
{
local swtpm_exe="$1"
local act_exe
if [[ "$(uname -s)" =~ Linux ]]; then
if [ -z "$(file "${swtpm_exe}" | grep ELF)" ]; then
act_exe="$(dirname "${swtpm_exe}")"/.libs/"$(basename "${swtpm_exe}")"
else
act_exe="${swtpm_exe}"
fi
if [ -n "$(nm "${act_exe}" | grep __asan_)" ]; then
echo "${act_exe} is built with ASAN"
exit 77
fi
fi
}