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