1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
# shellcheck source=test/units/util.sh
. "$(dirname "$0")"/util.sh
export SYSTEMD_LOG_LEVEL=debug
SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
if [[ ! -x "${SD_PCREXTEND:?}" ]] || ! tpm_has_pcr sha256 16 || ! tpm_has_pcr sha256 15; then
echo "$SD_PCREXTEND or PCR sysfs files not found, skipping PCR extension tests"
exit 0
fi
at_exit() {
if [[ $? -ne 0 ]]; then
# Dump the event log on fail, to make debugging a bit easier
jq --seq --slurp </run/log/systemd/tpm2-measure.log
fi
}
trap at_exit EXIT
# Temporarily override sd-pcrextend's sanity checks
export SYSTEMD_FORCE_MEASURE=1
"$SD_PCREXTEND" --help
"$SD_PCREXTEND" --version
"$SD_PCREXTEND" --pcr=16 foo
"$SD_PCREXTEND" --machine-id
"$SD_PCREXTEND" --product-id
"$SD_PCREXTEND" --tpm2-device=list
"$SD_PCREXTEND" --tpm2-device=auto --pcr=16 foo
"$SD_PCREXTEND" --tpm2-device=/dev/tpm0 --pcr=16 foo
"$SD_PCREXTEND" --bank=sha256 --pcr=16 foo
"$SD_PCREXTEND" --bank=sha256 --bank=sha256 --pcr=16 foo
"$SD_PCREXTEND" --graceful --pcr=16 foo
"$SD_PCREXTEND" --pcr=15 foo
"$SD_PCREXTEND" --file-system=/
"$SD_PCREXTEND" --file-system=/tmp --file-system=/
"$SD_PCREXTEND" --file-system=/tmp --file-system=/ --pcr=15 --pcr=16
"$SD_PCREXTEND" --nvpcr=hardware foo
if tpm_has_pcr sha1 16; then
"$SD_PCREXTEND" --bank=sha1 --pcr=16 foo
fi
(! "$SD_PCREXTEND")
(! "$SD_PCREXTEND" "")
(! "$SD_PCREXTEND" foo bar)
(! "$SD_PCREXTEND" --bank= foo)
(! "$SD_PCREXTEND" --tpm2-device= foo)
(! "$SD_PCREXTEND" --tpm2-device=/dev/null foo)
(! "$SD_PCREXTEND" --pcr= foo)
(! "$SD_PCREXTEND" --pcr=-1 foo)
(! "$SD_PCREXTEND" --pcr=1024 foo)
(! "$SD_PCREXTEND" --foo=bar)
(! "$SD_PCREXTEND" --nvpcr=idontexist foo)
unset SYSTEMD_FORCE_MEASURE
# Note: since we're reading the TPM event log as json-seq, the same rules apply to the output
# as well, i.e. each record is prefixed by RS (0x1E, 036) and suffixed by LF (0x0A, 012).
# LF is usually eaten by bash, but RS needs special handling.
# Save the number of events in the current event log, so we can skip them when
# checking changes caused by following tests
RECORD_COUNT="$(jq --seq --slurp '. | length' </run/log/systemd/tpm2-measure.log | tr -d '\036')"
# Let's measure the machine ID
tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
mv /etc/machine-id /etc/machine-id.save
echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --machine-id
mv /etc/machine-id.save /etc/machine-id
tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
# And check it matches expectations
diff /tmp/newpcr15 \
<(cat /tmp/oldpcr15 <(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
# Check that the event log record was properly written
test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e15')"
DIGEST_EXPECTED="$(echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -hex -sha256 -r)"
DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
RECORD_COUNT=$((RECORD_COUNT + 1))
# And similar for a string measurement into PCR 16
tpm2_pcrread sha256:16 -Q -o /tmp/oldpcr16
# Do the equivalent of 'SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --pcr=16 foobar' via Varlink, just to test the Varlink logic (but first we need to patch out the conditionalization...)
mkdir -p /run/systemd/system/systemd-pcrextend.socket.d
cat >/run/systemd/system/systemd-pcrextend.socket.d/50-no-condition.conf <<EOF
[Unit]
# Turn off all conditions */
ConditionSecurity=
EOF
systemctl daemon-reload
systemctl restart systemd-pcrextend.socket
varlinkctl call /run/systemd/io.systemd.PCRExtend io.systemd.PCRExtend.Extend '{"pcr":16,"text":"foobar"}'
tpm2_pcrread sha256:16 -Q -o /tmp/newpcr16
diff /tmp/newpcr16 \
<(cat /tmp/oldpcr16 <(echo -n "foobar" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
# Check the event log for the 2nd new record since $RECORD_COUNT
test "$(jq --seq --slurp ".[$RECORD_COUNT].pcr" </run/log/systemd/tpm2-measure.log)" == "$(printf '\x1e16')"
DIGEST_EXPECTED="$(echo -n "foobar" | openssl dgst -hex -sha256 -r)"
DIGEST_CURRENT="$(jq --seq --slurp --raw-output ".[$RECORD_COUNT].digests[] | select(.hashAlg == \"sha256\").digest" </run/log/systemd/tpm2-measure.log) *stdin"
test "$DIGEST_EXPECTED" == "$DIGEST_CURRENT"
# Measure a file system into PCR 15
tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
SYSTEMD_FORCE_MEASURE=1 "$SD_PCREXTEND" --file-system=/
# Put together the "file system word" we just sent to the TPM
# file-system:MOUNTPOINT:TYPE:UUID:LABEL:PART_ENTRY_UUID:PART_ENTRY_TYPE:PART_ENTRY_NAME
ROOT_DEVICE="$(findmnt -n -o SOURCE /)"
FS_WORD="$(lsblk -n -o MOUNTPOINT,FSTYPE,UUID,LABEL,PARTUUID,PARTTYPE,PARTLABEL "$ROOT_DEVICE" | sed -r 's/[ ]+/:/g')"
tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
# And check if it matches with the current PCR 15 state
diff /tmp/newpcr15 \
<(cat /tmp/oldpcr15 <(echo -n "file-system:$FS_WORD" | openssl dgst -binary -sha256) | openssl dgst -binary -sha256)
rm -f /tmp/oldpcr{16,15} /tmp/newpcr{16,15}
|