| #!/usr/bin/env bash |
| #set -x |
| |
| # For the license, see the LICENSE file in the root directory. |
| |
| # shellcheck disable=SC1091 |
| |
| if [ "$(id -u)" -ne 0 ]; then |
| echo "Need to be root to run this test." |
| exit 77 |
| fi |
| |
| # tpmtool is not packaged everywhere ... |
| if [ -z "$(type -P tpmtool)" ]; then |
| echo "Could not find tpmtool in PATH" |
| exit 77 |
| fi |
| |
| ROOT=${abs_top_builddir:-$(dirname "$0")/..} |
| TESTDIR=${abs_top_testdir:=$(dirname "$0")} |
| SRCDIR=${abs_top_srcdir:-$(dirname "$0")/..} |
| |
| PATH=$ROOT/src/swtpm:$PATH |
| |
| source "${abs_top_builddir:-$(dirname "$0")/..}/tests/test_config" |
| |
| SWTPM_SETUP=${ROOT}/src/swtpm_setup/swtpm_setup |
| SWTPM_CREATE_TPMCA=${SRCDIR}/samples/swtpm-create-tpmca |
| SWTPM_LOCALCA=${ROOT}/src/swtpm_localca/swtpm_localca |
| SWTPM=${ROOT}/src/swtpm/swtpm |
| SWTPM_IOCTL=${ROOT}/src/swtpm_ioctl/swtpm_ioctl |
| |
| SWTPM_INTERFACE=socket+socket |
| SWTPM_SERVER_NAME=localhost |
| SWTPM_SERVER_PORT=65434 |
| SWTPM_CTRL_PORT=65435 |
| |
| TCSD_LISTEN_PORT=65436 |
| |
| SRK_PASSWORD=srk |
| OWNER_PASSWORD=owner |
| |
| workdir="$(mktemp -d)" || exit 1 |
| |
| TCSD_CONF=${workdir}/tcsd.conf |
| TCSD_SYSTEM_PS_FILE=${workdir}/system_ps_file |
| TCSD_PIDFILE=${workdir}/tcsd.pid |
| SWTPM_LOCALCA_DIR="${workdir}/my localca" |
| SWTPM_LOCALCA_CONF="${workdir}/my localca/swtpm-localca.conf" |
| |
| # Captured TCSD file when using a SRK_PASSWORD=srk |
| TCSD_FILE="AQEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAABLwEAAAAAAwAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAQEAAAARAAAAAAEAAAABAAMAAQAAAAwAAAgAAAAAAgAAAAAAAAAA |
| AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
| AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" |
| |
| function cleanup() |
| { |
| if [ -n "${TCSD_PID}" ]; then |
| kill_quiet -15 "${TCSD_PID}" |
| fi |
| if [ -n "${SWTPM_PID}" ]; then |
| kill_quiet -9 "${SWTPM_PID}" |
| fi |
| if [ -n "${BASH_PID}" ]; then |
| kill_quiet -9 "${BASH_PID}" |
| fi |
| rm -rf "${workdir}" |
| } |
| |
| trap "cleanup" SIGTERM EXIT |
| source "${TESTDIR}/common" |
| skip_test_no_tpm12 "${SWTPM_EXE}" |
| |
| PATH=${ROOT}/src/swtpm_bios:${ROOT}/src/swtpm_cert:${PATH} |
| |
| # run the test with the given owner and SRK passwords |
| # @param1: owner password; empty means to use well known password |
| # @param2: SRK password; empty means to use well known password |
| # @param3: vTPM is a TPM 2.0 |
| function run_test() { |
| local owner_password="$1" |
| local srk_password="$2" |
| local vtpm_is_tpm2="$3" |
| |
| local params certinfo regex regexs fil i skip |
| |
| rm -rf "${workdir:?}"/* |
| |
| cat <<_EOF_ > "${workdir}/swtpm_setup.conf" |
| create_certs_tool=${SWTPM_LOCALCA} |
| create_certs_tool_config=${workdir}/swtpm-localca.conf |
| create_certs_tool_options=/dev/null |
| _EOF_ |
| |
| params="" |
| if [ -n "${owner_password}" ]; then |
| params="${params} --ownerpass ${owner_password}" |
| else |
| params="${params} --owner-well-known" |
| fi |
| params="${params} --srkpass ${srk_password}" |
| |
| # First setup the TPM and take ownership of it and set SRK password |
| if ! $SWTPM_SETUP \ |
| --runas root \ |
| --tpm-state "${workdir}" \ |
| --logfile "${workdir}/logfile" \ |
| --config "${workdir}/swtpm_setup.conf" \ |
| --tpm "${SWTPM_EXE} socket ${SWTPM_TEST_SECCOMP_OPT}" \ |
| --take-ownership \ |
| ${params:+${params}} >/dev/null; then |
| echo "Error: Could not run $SWTPM_SETUP." |
| echo "Setup Logfile:" |
| cat "${workdir}/logfile" |
| exit 1 |
| fi |
| |
| echo "Successfully took ownership of TPM and set owner and SRK passwords." |
| |
| run_swtpm "${SWTPM_INTERFACE}" \ |
| --flags not-need-init \ |
| --tpmstate "dir=${workdir}" |
| |
| # Startup the TPM |
| res="$(swtpm_cmd_tx "${SWTPM_INTERFACE}" '\x00\xC1\x00\x00\x00\x0C\x00\x00\x00\x99\x00\x01')" |
| exp=' 00 c4 00 00 00 0a 00 00 00 00' |
| if [ "$res" != "$exp" ]; then |
| echo "Error: Did not get expected result from TPM_Startup(ST_Clear)" |
| echo "expected: $exp" |
| echo "received: $res" |
| exit 1 |
| fi |
| |
| echo "$TCSD_FILE" | base64 -d > "${TCSD_SYSTEM_PS_FILE}" |
| |
| # Setup the TCSD config file and start TCSD with it |
| cat <<_EOF_ > "${TCSD_CONF}" |
| port = ${TCSD_LISTEN_PORT} |
| system_ps_file = ${TCSD_SYSTEM_PS_FILE} |
| _EOF_ |
| |
| # Due to recent changes in tcsd we have to try with TSS_USER=tss and TSS_USER=root |
| # Before the following worked: |
| # - tss:tss 0600 for TSS_USER=tss and TSS_GROUP=tss |
| # - root:tss 0640 for TSS_USER=root and TSS_GROUP=tss |
| # After the changes: |
| # - root:tss 0640 for TSS_USER=tss and TSS_GROUP=tss |
| while :; do |
| chown "${TSS_USER}:${TSS_GROUP}" "${TCSD_CONF}" |
| if [ "${TSS_USER}" == "${TSS_GROUP}" ]; then |
| chmod 0600 "${TCSD_CONF}" |
| else |
| chmod 0640 "${TCSD_CONF}" |
| fi |
| |
| 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 "Error: Could not get TCSD's PID file" |
| exit 1 |
| fi |
| |
| # wait for PID to be written |
| sleep 0.5 |
| TCSD_PID=$(cat "${TCSD_PIDFILE}") |
| if ! kill_quiet -0 "${TCSD_PID}"; then |
| # Try again with root unless we already tried |
| if [ "$TSS_USER" != "root" ]; then |
| TSS_USER="root" |
| continue |
| fi |
| echo "Error: TCSD with pid ${TCSD_PID} must have terminated" |
| exit 1 |
| fi |
| break |
| done |
| |
| if ! ${SWTPM_CREATE_TPMCA} \ |
| --dir "${SWTPM_LOCALCA_DIR}" \ |
| --srk-password "${srk_password}" \ |
| --register \ |
| --group "${TSS_GROUP}" \ |
| --tss-tcsd-port "${TCSD_LISTEN_PORT}" \ |
| --outfile "${SWTPM_LOCALCA_CONF}" &>/dev/null; then |
| echo "Error: Could not create TPM CA" |
| exit 1 |
| fi |
| |
| for fil in \ |
| swtpm-localca-rootca-cert.pem \ |
| swtpm-localca-rootca-privkey.pem \ |
| swtpm-localca-tpmca-cert.pem \ |
| swtpm-localca-tpmca-pubkey.pem; do |
| if [ ! -r "${SWTPM_LOCALCA_DIR}/${fil}" ]; then |
| echo "Error: TPM CA tool did not create file ${fil}." |
| exit 1 |
| fi |
| done |
| |
| params="" |
| if [ -n "${srk_password}" ]; then |
| params="^parentkey_password =" |
| fi |
| |
| for regex in \ |
| "^statedir = " \ |
| "^signingkey = " \ |
| "^issuercert = " \ |
| "^certserial = " \ |
| "^TSS_TCSD_HOSTNAME = " \ |
| "^TSS_TCSD_PORT = " \ |
| ${params}; do |
| if [ -n "${regex}" ] && \ |
| ! grep -q -E "${regex}" "${SWTPM_LOCALCA_CONF}"; then |
| echo "Error: Could not find regex '${regex}' in CA config file." |
| cat "${SWTPM_LOCALCA_CONF}" |
| exit 1 |
| fi |
| done |
| |
| params="" |
| if [ "${vtpm_is_tpm2}" -ne 0 ]; then |
| params="--tpm2" |
| skip=0 |
| else |
| skip=7 # header in cert |
| fi |
| |
| # make sure we can actually sign with this new certificate |
| if ! ${SWTPM_LOCALCA} \ |
| --type ek \ |
| --ek x=739192d8f1004283957a7b1568d610b41c637ccc114aadcac4908c20456468fa,y=59f63ac06f8011f6fdd1460c6bc8e3e0a2d090d4fc188c7e04870e06795ce8ae \ |
| --dir "${workdir}" --vmid test \ |
| ${params} \ |
| --tpm-spec-family 2.0 --tpm-spec-revision 146 --tpm-spec-level 00 \ |
| --tpm-model swtpm --tpm-version 20170101 --tpm-manufacturer IBM \ |
| --configfile "${SWTPM_LOCALCA_CONF}" \ |
| --optsfile /dev/null; then |
| echo "Error: The CA could not sign with the new certificate" |
| exit 1 |
| fi |
| if [ ! -f "${workdir}/ek.cert" ]; then |
| echo "Error: The CA did not produce a certificate" |
| exit 1 |
| fi |
| # cert was for example 541 bytes long |
| if [ "$(get_filesize "${workdir}/ek.cert")" -lt 500 ]; then |
| echo "Error: The certificate's size is dubious" |
| ls -l "${workdir}/ek.cert" |
| exit 1 |
| fi |
| |
| # Check the contents of the certificate |
| certinfo=$(dd "if=${workdir}/ek.cert" bs=1 "skip=$skip" status=none | \ |
| "$CERTTOOL" -i --inder) |
| regexs=('^[[:space:]]+2.23.133.8.1$' |
| '^[[:space:]]+directoryName:.*(,)?2.23.133.2.3=.*' |
| '^[[:space:]]+directoryName:.*(,)?2.23.133.2.2=.*' |
| '^[[:space:]]+directoryName:.*(,)?2.23.133.2.1=.*' |
| '^[[:space:]]+Certificate Authority \(CA\): FALSE$' |
| '^[[:space:]]+Unknown extension 2.5.29.9 \(not critical\):$' |
| '^[[:space:]]+Hexdump: 3019301706056781050210310e300c0c03322e3002010002020092$') |
| if [ "${vtpm_is_tpm2}" -ne 0 ]; then |
| # TPM 2.0; due to ecc: Key agreement |
| regexs+=('^[[:space:]]+Key agreement\.$' |
| '^[[:space:]]+Signature Algorithm: RSA-SHA256$') |
| else |
| regexs+=('^[[:space:]]+Key encipherment\.$' |
| '^[[:space:]]+Signature Algorithm: RSA-SHA1$') |
| fi |
| |
| for ((i=0; i < ${#regexs}; i++)); do \ |
| if [ -n "${regexs[$i]}" ] && \ |
| ! echo "${certinfo}" | grep -q -E "${regexs[$i]}"; then |
| echo "Error: Could not match regex '${regexs[$i]}' with certificate info:" |
| echo "${certinfo}" |
| exit 1 |
| fi |
| done |
| |
| # Send SIGTERM to TCSD |
| kill_quiet -15 "${TCSD_PID}" |
| |
| # Shut down TPM |
| if ! run_swtpm_ioctl "${SWTPM_INTERFACE}" -s; then |
| echo "Error: Could not shut down the ${SWTPM_INTERFACE} TPM." |
| exit 1 |
| fi |
| |
| if wait_process_gone "${SWTPM_PID}" 4; then |
| echo "Error: ${SWTPM_INTERFACE} TPM should not be running anymore." |
| exit 1 |
| fi |
| |
| if wait_process_gone "${SWTPM_PID}" 4; then |
| echo "Error: tcsd should not be running anymore." |
| exit 1 |
| fi |
| } # run_test |
| |
| run_test "${OWNER_PASSWORD}" "${SRK_PASSWORD}" 1 |
| echo "Test 1: OK" |
| |
| run_test "${OWNER_PASSWORD}" "${SRK_PASSWORD}" 0 |
| echo "Test 2: OK" |
| |
| run_test "" "${SRK_PASSWORD}" 1 |
| echo "Test 3: OK" |
| |
| run_test "" "${SRK_PASSWORD}" 0 |
| echo "Test 4: OK" |
| |
| exit 0 |