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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
#!/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
export PAGER=
SD_PCREXTEND="/usr/lib/systemd/systemd-pcrextend"
SD_PCRLOCK="/usr/lib/systemd/systemd-pcrlock"
SD_MEASURE="/usr/lib/systemd/systemd-measure"
if [[ ! -x "${SD_PCREXTEND:?}" ]] || [[ ! -x "${SD_PCRLOCK:?}" ]] || [[ ! -x "${SD_MEASURE:?}" ]] ; then
echo "$SD_PCREXTEND or $SD_PCRLOCK or $SD_MEASURE not found, skipping pcrlock tests"
exit 0
fi
at_exit() {
if [[ $? -ne 0 ]]; then
# Dump the event log on fail, to make debugging a bit easier
[[ -e /run/log/systemd/tpm2-measure.log ]] && jq --seq --slurp </run/log/systemd/tpm2-measure.log
fi
return 0
}
trap at_exit EXIT
# Temporarily override sd-pcrextend's sanity checks
export SYSTEMD_FORCE_MEASURE=1
# The PCRs we are going to lock to. We exclude the various PCRs we touched
# above where no event log record was written, because we cannot analyze
# things without event log. We include debug PCR 16, see below.
PCRS="1+2+3+4+5+16"
# Remove the old measurement log, as it contains all kinds of nonsense from the
# previous test, which will fail our consistency checks. Removing the file also
# means we'll fail consistency check, but at least we'll fail them consistently
# (as the PCR values simply won't match the log).
rm -f /run/log/systemd/tpm2-measure.log
# Reset TPM PCR 16 ("debug") explicitly, so that we can use it in a known good state
tpm2_pcrreset 16
# Ensure a truncated log doesn't crash pcrlock
echo -n -e \\x1e >/tmp/borked
set +e
SYSTEMD_MEASURE_LOG_USERSPACE=/tmp/borked "$SD_PCRLOCK" cel --no-pager --json=pretty
ret=$?
set -e
# If it crashes the exit code will be 149
test $ret -eq 1
SYSTEMD_COLORS=256 "$SD_PCRLOCK"
"$SD_PCRLOCK" is-supported
"$SD_PCRLOCK" cel --no-pager --json=pretty
"$SD_PCRLOCK" log --pcr="$PCRS"
"$SD_PCRLOCK" log --json=pretty --pcr="$PCRS"
"$SD_PCRLOCK" list-components
"$SD_PCRLOCK" list-components --location=250-
"$SD_PCRLOCK" list-components --location=250-:350-
"$SD_PCRLOCK" lock-firmware-config
"$SD_PCRLOCK" lock-gpt
"$SD_PCRLOCK" lock-machine-id
"$SD_PCRLOCK" lock-file-system
"$SD_PCRLOCK" lock-file-system /
"$SD_PCRLOCK" predict --pcr="$PCRS"
"$SD_PCRLOCK" predict --pcr="0x1+0x3+4"
"$SD_PCRLOCK" predict --json=pretty --pcr="$PCRS"
SD_STUB="$(find /usr/lib/systemd/boot/efi/ -name "systemd-boot*.efi" | head -n1)"
if [[ -n "$SD_STUB" ]]; then
"$SD_PCRLOCK" lock-pe "$SD_STUB"
"$SD_PCRLOCK" lock-pe <"$SD_STUB"
"$SD_PCRLOCK" lock-uki "$SD_STUB"
"$SD_PCRLOCK" lock-uki <"$SD_STUB"
fi
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
# Repeat immediately (this call will have to reuse the nvindex, rather than create it)
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
img="/tmp/pcrlock.img"
truncate -s 20M "$img"
echo -n hoho >/tmp/pcrlockpwd
chmod 0600 /tmp/pcrlockpwd
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$img" /tmp/pcrlockpwd
systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --tpm2-public-key= --wipe-slot=tpm2 "$img"
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
systemd-cryptsetup detach pcrlock
# Ensure systemd-pcrlock not crashing on empty variant directory
mkdir -p /var/lib/pcrlock.d/123-empty.pcrlock.d
"$SD_PCRLOCK" predict --pcr="$PCRS"
rm -rf /var/lib/pcrlock.d/123-empty.pcrlock.d
# Measure something into PCR 16 (the "debug" PCR), which should make the activation fail
"$SD_PCREXTEND" --pcr=16 test70
"$SD_PCRLOCK" cel --json=pretty
(! systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless )
# Now add a component for it, rebuild policy and it should work (we'll rebuild
# once like that, but don't provide the recovery pin. This should fail, since
# the PCR is hosed after all. But then we'll use recovery pin, and it should
# work.
echo -n test70 | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock --pcr=16
(! "$SD_PCRLOCK" make-policy --pcr="$PCRS")
PIN=huhu "$SD_PCRLOCK" make-policy --pcr="$PCRS" --recovery-pin=query
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
systemd-cryptsetup detach pcrlock
# And now let's do it the clean way, and generate the right policy ahead of time.
echo -n test70-take-two | "$SD_PCRLOCK" lock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock --pcr=16
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
# the next one should be skipped because redundant
"$SD_PCRLOCK" make-policy --pcr="$PCRS"
# but this one should not be skipped, even if redundant, because we force it
"$SD_PCRLOCK" make-policy --pcr="$PCRS" --force --recovery-pin=show
"$SD_PCREXTEND" --pcr=16 test70-take-two
"$SD_PCRLOCK" cel --json=pretty
systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,headless
systemd-cryptsetup detach pcrlock
# Now combined pcrlock and signed PCR
# Generate key pair
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out "$img".private.pem
openssl rsa -pubout -in "$img".private.pem -out "$img".public.pem
systemd-cryptenroll --unlock-tpm2-device=auto --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --tpm2-public-key="$img".public.pem --wipe-slot=tpm2 "$img"
"$SD_MEASURE" sign --current --bank=sha256 --private-key="$img".private.pem --public-key="$img".public.pem --phase=: | tee "$img".pcrsign
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 systemd-cryptsetup attach pcrlock "$img" - "tpm2-device=auto,tpm2-pcrlock=/var/lib/systemd/pcrlock.json,tpm2-signature=$img.pcrsign,headless"
systemd-cryptsetup detach pcrlock
systemd-cryptenroll --unlock-key-file=/tmp/pcrlockpwd --tpm2-device=auto --tpm2-pcrlock=/var/lib/systemd/pcrlock.json --tpm2-public-key= --wipe-slot=tpm2 "$img"
rm "$img".public.pem "$img".private.pem "$img".pcrsign
# Now use the root fs support, i.e. make the tool write a copy of the pcrlock
# file as service credential to some temporary dir and remove the local copy, so that
# it has to use the credential version.
mkdir /tmp/fakexbootldr
SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" make-policy --pcr="$PCRS" --force
mv /var/lib/systemd/pcrlock.json /var/lib/systemd/pcrlock.json.gone
ls -al /tmp/fakexbootldr/loader/credentials
CREDENTIAL_FILE="$(echo /tmp/fakexbootldr/loader/credentials/pcrlock.*.cred)"
test -f "$CREDENTIAL_FILE"
# Strip dir and .cred suffix from file name.
CREDENTIAL_NAME=${CREDENTIAL_FILE#/tmp/fakexbootldr/loader/credentials/}
CREDENTIAL_NAME=${CREDENTIAL_NAME%.cred}
# If SB is enabled then this will fail as it's not locked but TPM2 is enabled
if cmp /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c <(printf '\6\0\0\0\1'); then
ALLOW_NULL=--allow-null
fi
systemd-creds decrypt "${ALLOW_NULL:-}" --name="$CREDENTIAL_NAME" "$CREDENTIAL_FILE"
ln -s "$CREDENTIAL_FILE" /tmp/fakexbootldr/loader/credentials/"$CREDENTIAL_NAME"
test -f /tmp/fakexbootldr/loader/credentials/"$CREDENTIAL_NAME"
SYSTEMD_ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY=/tmp/fakexbootldr/loader/credentials systemd-cryptsetup attach pcrlock "$img" - tpm2-device=auto,headless
systemd-cryptsetup detach pcrlock
mv /var/lib/systemd/pcrlock.json.gone /var/lib/systemd/pcrlock.json
SYSTEMD_XBOOTLDR_PATH=/tmp/fakexbootldr SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 "$SD_PCRLOCK" remove-policy
"$SD_PCRLOCK" unlock-firmware-config
"$SD_PCRLOCK" unlock-gpt
"$SD_PCRLOCK" unlock-machine-id
"$SD_PCRLOCK" unlock-file-system
"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/910-test70.pcrlock
"$SD_PCRLOCK" unlock-raw --pcrlock=/var/lib/pcrlock.d/920-test70.pcrlock
(! "$SD_PCRLOCK" "")
(! "$SD_PCRLOCK" predict --pcr=-1)
(! "$SD_PCRLOCK" predict --pcr=foo)
(! "$SD_PCRLOCK" predict --pcr=1+1)
(! "$SD_PCRLOCK" predict --pcr=1+++++1)
(! "$SD_PCRLOCK" make-policy --nv-index=0)
(! "$SD_PCRLOCK" make-policy --nv-index=foo)
(! "$SD_PCRLOCK" list-components --location=:)
(! "$SD_PCRLOCK" lock-gpt "")
(! "$SD_PCRLOCK" lock-gpt /dev/sr0)
(! "$SD_PCRLOCK" lock-pe /dev/full)
(! "$SD_PCRLOCK" lock-pe /bin/true)
(! "$SD_PCRLOCK" lock-uki /dev/full)
(! "$SD_PCRLOCK" lock-uki /bin/true)
(! "$SD_PCRLOCK" lock-file-system "")
# Exercise Varlink API a bit (but first turn off condition)
mkdir -p /run/systemd/system/systemd-pcrlock.socket.d
cat >/run/systemd/system/systemd-pcrlock.socket.d/50-no-condition.conf <<EOF
[Unit]
# Turn off all conditions
ConditionSecurity=
EOF
systemctl daemon-reload
systemctl restart systemd-pcrlock.socket
varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.RemovePolicy '{}'
varlinkctl call /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.MakePolicy '{}'
varlinkctl call --collect --json=pretty /run/systemd/io.systemd.PCRLock io.systemd.PCRLock.ReadEventLog '{}'
rm "$img" /tmp/pcrlockpwd
# For issue #35746
for _ in {0..10}; do
run0 /usr/lib/systemd/systemd-pcrlock
done
|