xref: /aosp_15_r20/external/ltp/testcases/kernel/security/integrity/ima/tests/ima_tpm.sh (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
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 <[email protected]>
5# Author: Mimi Zohar <[email protected]>
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_res TFAIL "evmctl failed $msg"
163		return
164	fi
165
166	[ $ret -ne 0 ] && tst_res TWARN "evmctl failed, trying to continue $msg"
167
168	pcr=$(grep -E "^($ALGORITHM: )*PCRAgg(.*10)*:" hash.txt | tail -1 \
169		| awk '{print $NF}')
170
171	if [ -z "$pcr" ]; then
172		tst_res $res "failed to find aggregate PCR-10 $msg"
173		tst_res TINFO "hash file:"
174		cat hash.txt >&2
175		return
176	fi
177
178	echo "$pcr"
179}
180
181test1_tpm_bypass_mode()
182{
183	local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}')
184
185	if [ "$DIGEST" = "$zero" ]; then
186		tst_res TPASS "bios boot aggregate is $zero"
187	else
188		tst_res TFAIL "bios boot aggregate is not $zero ($DIGEST), kernel didn't export TPM 2.0 files for TPM device?"
189		return 1
190	fi
191}
192
193test1_hw_tpm()
194{
195	local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements"
196	local cmd="evmctl ima_boot_aggregate -v"
197	local boot_aggregate
198
199	if [ -z "$TPM_VERSION" ]; then
200		tst_res TWARN "TPM-bypass failed, trying to test TPM device (unknown TPM version)"
201		MAYBE_TPM2=1
202	fi
203
204	if [ "$MISSING_EVMCTL" = 1 ]; then
205		if [ ! -f "$tpm_bios" ]; then
206			tst_res TCONF "missing $tpm_bios $ERRMSG_EVMCTL"
207			return
208		fi
209		tst_check_cmds ima_boot_aggregate || return
210
211		cmd="ima_boot_aggregate -f $tpm_bios"
212
213		# TCONF: libcrypto and openssl development packages required
214		$cmd
215		if [ $? -eq 32 ]; then
216			tst_res TCONF "$cmd returned TCONF"
217			return
218		fi
219	fi
220	tst_res TINFO "using command: $cmd"
221
222	boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2)
223	if [ -z "$boot_aggregate" ]; then
224		tst_res TFAIL "failed to get boot aggregate"
225		return
226	fi
227	tst_res TINFO "IMA boot aggregate: '$boot_aggregate'"
228
229	if [ "$DIGEST" = "$boot_aggregate" ]; then
230		tst_res TPASS "bios boot aggregate matches IMA boot aggregate"
231	else
232		tst_res TFAIL "bios boot aggregate does not match IMA boot aggregate ($DIGEST)"
233	fi
234}
235
236test1()
237{
238	tst_res TINFO "verify boot aggregate"
239
240	# deliberately try test1_hw_tpm() if test1_tpm_bypass_mode() fails
241	[ -z "$TPM_VERSION" ] && test1_tpm_bypass_mode || test1_hw_tpm
242}
243
244test2()
245{
246	local hash pcr_aggregate out ret
247
248	tst_res TINFO "verify PCR values"
249
250	if [ "$MAYBE_TPM2" = 1 ]; then
251		tst_res TINFO "TPM version not detected ($ERRMSG_TPM), assume TPM 2"
252		TPM_VERSION=2
253	fi
254
255	if [ -z "$TPM_VERSION" ]; then
256		tst_brk TCONF "TPM version not detected ($ERRMSG_TPM)"
257	fi
258
259	if [ "$ALGORITHM" = "sha1" -a "$MISSING_EVMCTL" = 1 ]; then
260		tst_check_cmds evmctl || return 1
261	fi
262
263	out=$(read_pcr_tpm$TPM_VERSION)
264	ret=$?
265
266	if [ $ret -ne 0 ]; then
267		case "$ret" in
268			1) tst_res TFAIL "$out";;
269			32) tst_res TCONF "$out";;
270			*) tst_brk TBROK "unsupported return type '$1'";;
271		esac
272		return
273	fi
274
275	hash=$(echo "$out" | grep "^PCR-10" | cut -d' ' -f2)
276
277	if [ -z "$out" ]; then
278		tst_res TFAIL "PCR-10 hash not found"
279		return
280	fi
281
282	tst_res TINFO "real PCR-10: '$hash'"
283
284	get_pcr10_aggregate > tmp.txt
285	pcr_aggregate="$(cat tmp.txt)"
286	if [ -z "$pcr_aggregate" ]; then
287		return
288	fi
289	tst_res TINFO "aggregate PCR-10: '$pcr_aggregate'"
290
291	if [ "$hash" = "$pcr_aggregate" ]; then
292		tst_res TPASS "aggregate PCR value matches real PCR value"
293	else
294		tst_res TFAIL "aggregate PCR value does not match real PCR value"
295	fi
296}
297
298. ima_setup.sh
299tst_run
300