• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-or-later
3# Copyright (c) 2009 IBM Corporation
4# Copyright (c) 2018-2020 Petr Vorel <pvorel@suse.cz>
5# Author: Mimi Zohar <zohar@linux.ibm.com>
6#
7# Verify the boot and PCR aggregates.
8
9TST_CNT=2
10TST_NEEDS_CMDS="awk cut tail"
11TST_SETUP="setup"
12
13. ima_setup.sh
14
15EVMCTL_REQUIRED='1.3.1'
16ERRMSG_EVMCTL="=> install evmctl >= $EVMCTL_REQUIRED"
17ERRMSG_TPM="TPM hardware support not enabled in kernel or no TPM chip found"
18
19setup()
20{
21	local config="${KCONFIG_PATH:-/boot/config-$(uname -r)}"
22	local line tmp
23
24	read line < $ASCII_MEASUREMENTS
25	if tmp=$(get_algorithm_digest "$line"); then
26		ALGORITHM=$(echo "$tmp" | cut -d'|' -f1)
27		DIGEST=$(echo "$tmp" | cut -d'|' -f2)
28	else
29		tst_brk TBROK "failed to get algorithm/digest: $tmp"
30	fi
31	tst_res TINFO "used algorithm: $ALGORITHM"
32
33	TPM_VERSION="$(get_tpm_version)"
34	if [ -z "$TPM_VERSION" ]; then
35		tst_res TINFO "$ERRMSG_TPM, testing TPM-bypass"
36	else
37		tst_res TINFO "TPM major version: $TPM_VERSION"
38	fi
39
40	if ! check_evmctl $EVMCTL_REQUIRED; then
41		if [ "$ALGORITHM" != "sha1" ]; then
42			tst_brk TCONF "algorithm not sha1 ($ALGORITHM) $ERRMSG_EVMCTL"
43		fi
44		MISSING_EVMCTL=1
45	fi
46
47	if [ -r "$config" ]; then
48		tst_res TINFO "TPM kernel config:"
49		for i in $(grep -e ^CONFIG_.*_TPM -e ^CONFIG_TCG $config); do
50			tst_res TINFO "$i"
51		done
52	fi
53}
54
55# prints major version: 1: TPM 1.2, 2: TPM 2.0
56# or nothing on TPM-bypass (no TPM device)
57# WARNING: Detecting TPM 2.0 can fail due kernel not exporting TPM 2.0 files.
58get_tpm_version()
59{
60	if [ -f /sys/class/tpm/tpm0/tpm_version_major ]; then
61		cat /sys/class/tpm/tpm0/tpm_version_major
62		return
63	fi
64
65	if [ -f /sys/class/tpm/tpm0/device/caps -o \
66		-f /sys/class/misc/tpm0/device/caps ]; then
67		echo 1
68		return
69	fi
70
71	if [ -c /dev/tpmrm0 -a -c /dev/tpm0 ]; then
72		echo 2
73		return
74	fi
75
76	if [ ! -d /sys/class/tpm/tpm0/ -a ! -d /sys/class/misc/tpm0/ ]; then
77		return
78	fi
79
80	tst_require_cmds dmesg
81	if dmesg | grep -q 'activating TPM-bypass'; then
82		return
83	elif dmesg | grep -q '1\.2 TPM (device-id'; then
84		echo 1
85		return
86	elif dmesg | grep -q '2\.0 TPM (device-id'; then
87		echo 2
88		return
89	fi
90}
91
92read_pcr_tpm1()
93{
94	local pcrs_path="/sys/class/tpm/tpm0/device/pcrs"
95	local evmctl_required="1.1"
96	local hash pcr
97
98	if [ ! -f "$pcrs_path" ]; then
99		pcrs_path="/sys/class/misc/tpm0/device/pcrs"
100	elif ! check_evmctl $evmctl_required; then
101		echo "evmctl >= $evmctl_required required"
102		return 32
103	fi
104
105	if [ ! -f "$pcrs_path" ]; then
106		echo "missing PCR file $pcrs_path ($ERRMSG_TPM)"
107		return 32
108	fi
109
110	while read line; do
111		pcr="$(echo $line | cut -d':' -f1)"
112		hash="$(echo $line | cut -d':' -f2 | awk '{ gsub (" ", "", $0); print tolower($0) }')"
113		echo "$pcr: $hash"
114	done < $pcrs_path
115
116	return 0
117}
118
119# NOTE: TPM 1.2 would require to use tss1pcrread which is not fully adopted
120# by distros yet.
121read_pcr_tpm2()
122{
123	local pcrmax=23
124	local pcrread="tsspcrread -halg $ALGORITHM"
125	local i pcr
126
127	if ! tst_cmd_available tsspcrread; then
128		echo "tsspcrread not found"
129		return 32
130	fi
131
132	for i in $(seq 0 $pcrmax); do
133		pcr=$($pcrread -ha "$i" -ns)
134		if [ $? -ne 0 ]; then
135			echo "tsspcrread failed: $pcr"
136			return 1
137		fi
138		printf "PCR-%02d: %s\n" $i "$pcr"
139	done
140
141	return 0
142}
143
144get_pcr10_aggregate()
145{
146	local cmd="evmctl -vv ima_measurement $BINARY_MEASUREMENTS"
147	local msg="$ERRMSG_EVMCTL"
148	local res=TCONF
149	local pcr ret
150
151	if [ -z "$MISSING_EVMCTL" ]; then
152		msg=
153		res=TFAIL
154	fi
155
156	$cmd > hash.txt 2>&1
157	ret=$?
158	if [ $ret -ne 0 -a -z "$MISSING_EVMCTL" ]; then
159		tst_res TFAIL "evmctl failed, trying with --ignore-violations"
160		cmd="$cmd --ignore-violations"
161		$cmd > hash.txt 2>&1
162		ret=$?
163	elif [ $ret -ne 0 -a "$MISSING_EVMCTL" = 1 ]; then
164		tst_brk TFAIL "evmctl failed $msg"
165	fi
166
167	[ $ret -ne 0 ] && tst_res TWARN "evmctl failed, trying to continue $msg"
168
169	pcr=$(grep -E "^($ALGORITHM: )*PCRAgg(.*10)*:" hash.txt | tail -1 \
170		| awk '{print $NF}')
171
172	if [ -z "$pcr" ]; then
173		tst_res $res "failed to find aggregate PCR-10 $msg"
174		tst_res TINFO "hash file:"
175		cat hash.txt >&2
176		return
177	fi
178
179	echo "$pcr"
180}
181
182test1_tpm_bypass_mode()
183{
184	local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}')
185
186	if [ "$DIGEST" = "$zero" ]; then
187		tst_res TPASS "bios boot aggregate is $zero"
188	else
189		tst_res TFAIL "bios boot aggregate is not $zero ($DIGEST), kernel didn't export TPM 2.0 files for TPM device?"
190		return 1
191	fi
192}
193
194test1_hw_tpm()
195{
196	local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements"
197	local cmd="evmctl ima_boot_aggregate -v"
198	local boot_aggregate
199
200	if [ -z "$TPM_VERSION" ]; then
201		tst_res TWARN "TPM-bypass failed, trying to test TPM device (unknown TPM version)"
202		MAYBE_TPM2=1
203	fi
204
205	if [ "$MISSING_EVMCTL" = 1 ]; then
206		if [ ! -f "$tpm_bios" ]; then
207			tst_res TCONF "missing $tpm_bios $ERRMSG_EVMCTL"
208			return
209		fi
210		tst_check_cmds ima_boot_aggregate || return
211
212		cmd="ima_boot_aggregate -f $tpm_bios"
213
214		# TCONF: libcrypto and openssl development packages required
215		$cmd
216		if [ $? -eq 32 ]; then
217			tst_res TCONF "$cmd returned TCONF"
218			return
219		fi
220	fi
221	tst_res TINFO "using command: $cmd"
222
223	boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2)
224	if [ -z "$boot_aggregate" ]; then
225		tst_res TFAIL "failed to get boot aggregate"
226		return
227	fi
228	tst_res TINFO "IMA boot aggregate: '$boot_aggregate'"
229
230	if [ "$DIGEST" = "$boot_aggregate" ]; then
231		tst_res TPASS "bios boot aggregate matches IMA boot aggregate"
232	else
233		tst_res TFAIL "bios boot aggregate does not match IMA boot aggregate ($DIGEST)"
234	fi
235}
236
237test1()
238{
239	tst_res TINFO "verify boot aggregate"
240
241	# deliberately try test1_hw_tpm() if test1_tpm_bypass_mode() fails
242	[ -z "$TPM_VERSION" ] && test1_tpm_bypass_mode || test1_hw_tpm
243}
244
245test2()
246{
247	local hash pcr_aggregate out ret
248
249	tst_res TINFO "verify PCR values"
250
251	if [ "$MAYBE_TPM2" = 1 ]; then
252		tst_res TINFO "TPM version not detected ($ERRMSG_TPM), assume TPM 2"
253		TPM_VERSION=2
254	fi
255
256	if [ -z "$TPM_VERSION" ]; then
257		tst_brk TCONF "TPM version not detected ($ERRMSG_TPM)"
258	fi
259
260	if [ "$ALGORITHM" = "sha1" -a "$MISSING_EVMCTL" = 1 ]; then
261		tst_check_cmds evmctl || return 1
262	fi
263
264	out=$(read_pcr_tpm$TPM_VERSION)
265	ret=$?
266
267	if [ $ret -ne 0 ]; then
268		case "$ret" in
269			1) tst_res TFAIL "$out";;
270			32) tst_res TCONF "$out";;
271			*) tst_brk TBROK "unsupported return type '$1'";;
272		esac
273		return
274	fi
275
276	hash=$(echo "$out" | grep "^PCR-10" | cut -d' ' -f2)
277
278	if [ -z "$out" ]; then
279		tst_res TFAIL "PCR-10 hash not found"
280		return
281	fi
282
283	tst_res TINFO "real PCR-10: '$hash'"
284
285	get_pcr10_aggregate > tmp.txt
286	pcr_aggregate="$(cat tmp.txt)"
287	if [ -z "$pcr_aggregate" ]; then
288		return
289	fi
290	tst_res TINFO "aggregate PCR-10: '$pcr_aggregate'"
291
292	if [ "$hash" = "$pcr_aggregate" ]; then
293		tst_res TPASS "aggregate PCR value matches real PCR value"
294	else
295		tst_res TFAIL "aggregate PCR value does not match real PCR value"
296	fi
297}
298
299tst_run
300