xref: /aosp_15_r20/external/ltp/testcases/kernel/security/integrity/ima/tests/ima_setup.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
7TST_TESTFUNC="test"
8TST_SETUP_CALLER="$TST_SETUP"
9TST_SETUP="ima_setup"
10TST_CLEANUP_CALLER="$TST_CLEANUP"
11TST_CLEANUP="ima_cleanup"
12TST_NEEDS_ROOT=1
13TST_MOUNT_DEVICE=1
14
15# TST_MOUNT_DEVICE can be unset, therefore specify explicitly
16TST_NEEDS_TMPDIR=1
17
18SYSFS="/sys"
19UMOUNT=
20TST_FS_TYPE="ext3"
21
22# TODO: find support for rmd128 rmd256 rmd320 wp256 wp384 tgr128 tgr160
23compute_digest()
24{
25	local algorithm="$1"
26	local file="$2"
27	local digest
28
29	digest="$(${algorithm}sum $file 2>/dev/null | cut -f1 -d ' ')"
30	if [ -n "$digest" ]; then
31		echo "$digest"
32		return 0
33	fi
34
35	digest="$(openssl $algorithm $file 2>/dev/null | cut -f2 -d ' ')"
36	if [ -n "$digest" ]; then
37		echo "$digest"
38		return 0
39	fi
40
41	# uncommon ciphers
42	local arg="$algorithm"
43	case "$algorithm" in
44	tgr192) arg="tiger" ;;
45	wp512) arg="whirlpool" ;;
46	esac
47
48	digest="$(rdigest --$arg $file 2>/dev/null | cut -f1 -d ' ')"
49	if [ -n "$digest" ]; then
50		echo "$digest"
51		return 0
52	fi
53	return 1
54}
55
56check_policy_readable()
57{
58	if [ ! -f $IMA_POLICY ]; then
59		tst_res TINFO "missing $IMA_POLICY (reboot or CONFIG_IMA_WRITE_POLICY=y required)"
60		return 1
61	fi
62	cat $IMA_POLICY > /dev/null 2>/dev/null
63}
64
65require_policy_readable()
66{
67	if [ ! -f $IMA_POLICY ]; then
68		tst_brk TCONF "missing $IMA_POLICY (reboot or CONFIG_IMA_WRITE_POLICY=y required)"
69	fi
70	if ! check_policy_readable; then
71		tst_brk TCONF "cannot read IMA policy (CONFIG_IMA_READ_POLICY=y required)"
72	fi
73}
74
75require_policy_writable()
76{
77	local err="IMA policy already loaded and kernel not configured to enable multiple writes to it (need CONFIG_IMA_WRITE_POLICY=y)"
78
79	[ -f $IMA_POLICY ] || tst_brk TCONF "$err"
80	# CONFIG_IMA_READ_POLICY
81	echo "" 2> log > $IMA_POLICY
82	grep -q "Device or resource busy" log && tst_brk TCONF "$err"
83}
84
85check_ima_policy_content()
86{
87	local pattern="$1"
88	local grep_params="${2--q}"
89
90	check_policy_readable || return 1
91	grep $grep_params "$pattern" $IMA_POLICY
92}
93
94require_ima_policy_content()
95{
96	local pattern="$1"
97	local grep_params="${2--q}"
98
99	require_policy_readable
100	if ! grep $grep_params "$pattern" $IMA_POLICY; then
101		tst_brk TCONF "IMA policy does not specify '$pattern'"
102	fi
103}
104
105check_ima_policy_cmdline()
106{
107	local policy="$1"
108	local i
109
110	grep -q "ima_$policy" /proc/cmdline && return
111	for i in $(cat /proc/cmdline); do
112		if echo "$i" | grep -q '^ima_policy='; then
113			echo "$i" | grep -q -e "|[ ]*$policy" -e "$policy[ ]*|" -e "=$policy" && return 0
114		fi
115	done
116	return 1
117}
118
119require_ima_policy_cmdline()
120{
121	local policy="$1"
122
123	check_ima_policy_cmdline $policy || \
124		tst_brk TCONF "IMA measurement tests require builtin IMA $policy policy (e.g. ima_policy=$policy kernel parameter)"
125}
126
127mount_helper()
128{
129	local type="$1"
130	local default_dir="$2"
131	local dir
132
133	dir="$(grep ^$type /proc/mounts | cut -d ' ' -f2 | head -1)"
134	[ -n "$dir" ] && { echo "$dir"; return; }
135
136	if ! mkdir -p $default_dir; then
137		tst_brk TBROK "failed to create $default_dir"
138	fi
139	if ! mount -t $type $type $default_dir; then
140		tst_brk TBROK "failed to mount $type"
141	fi
142	UMOUNT="$default_dir $UMOUNT"
143	echo $default_dir
144}
145
146print_ima_config()
147{
148	local config="${KCONFIG_PATH:-/boot/config-$(uname -r)}"
149	local i
150
151	if [ -r "$config" ]; then
152		tst_res TINFO "IMA kernel config:"
153		for i in $(grep ^CONFIG_IMA $config); do
154			tst_res TINFO "$i"
155		done
156	fi
157
158	tst_res TINFO "/proc/cmdline: $(cat /proc/cmdline)"
159}
160
161ima_setup()
162{
163	SECURITYFS="$(mount_helper securityfs $SYSFS/kernel/security)"
164
165	IMA_DIR="$SECURITYFS/ima"
166	[ -d "$IMA_DIR" ] || tst_brk TCONF "IMA not enabled in kernel"
167	ASCII_MEASUREMENTS="$IMA_DIR/ascii_runtime_measurements"
168	BINARY_MEASUREMENTS="$IMA_DIR/binary_runtime_measurements"
169	IMA_POLICY="$IMA_DIR/policy"
170
171	# hack to support running tests locally from ima/tests directory
172	if [ ! -d "$TST_DATAROOT" ]; then
173		TST_DATAROOT="$LTPROOT/../datafiles/$TST_ID/"
174	fi
175
176	print_ima_config
177
178	if [ "$TST_MOUNT_DEVICE" = 1 ]; then
179		tst_res TINFO "\$TMPDIR is on tmpfs => run on loop device"
180		cd "$TST_MNTPOINT"
181	fi
182
183	[ -n "$TST_SETUP_CALLER" ] && $TST_SETUP_CALLER
184}
185
186ima_cleanup()
187{
188	local dir
189
190	[ -n "$TST_CLEANUP_CALLER" ] && $TST_CLEANUP_CALLER
191
192	for dir in $UMOUNT; do
193		umount $dir
194	done
195}
196
197set_digest_index()
198{
199	DIGEST_INDEX=
200
201	local template="$(tail -1 $ASCII_MEASUREMENTS | cut -d' ' -f 3)"
202	local i word
203
204	# parse digest index
205	# https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use
206	case "$template" in
207	ima|ima-ng|ima-sig) DIGEST_INDEX=4 ;;
208	*)
209		# using ima_template_fmt kernel parameter
210		local IFS="|"
211		i=4
212		for word in $template; do
213			if [ "$word" = 'd' -o "$word" = 'd-ng' ]; then
214				DIGEST_INDEX=$i
215				break
216			fi
217			i=$((i+1))
218		done
219	esac
220
221	[ -z "$DIGEST_INDEX" ] && tst_brk TCONF \
222		"Cannot find digest index (template: '$template')"
223}
224
225get_algorithm_digest()
226{
227	local line="$1"
228	local delimiter=':'
229	local algorithm digest
230
231	if [ -z "$line" ]; then
232		echo "measurement record not found"
233		return 1
234	fi
235
236	[ -z "$DIGEST_INDEX" ] && set_digest_index
237	digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX)
238	if [ -z "$digest" ]; then
239		echo "digest not found (index: $DIGEST_INDEX, line: '$line')"
240		return 1
241	fi
242
243	if [ "${digest#*$delimiter}" != "$digest" ]; then
244		algorithm=$(echo "$digest" | cut -d $delimiter -f 1)
245		digest=$(echo "$digest" | cut -d $delimiter -f 2)
246	else
247		case "${#digest}" in
248		32) algorithm="md5" ;;
249		40) algorithm="sha1" ;;
250		*)
251			echo "algorithm must be either md5 or sha1 (digest: '$digest')"
252			return 1 ;;
253		esac
254	fi
255	if [ -z "$algorithm" ]; then
256		echo "algorithm not found"
257		return 1
258	fi
259	if [ -z "$digest" ]; then
260		echo "digest not found"
261		return 1
262	fi
263
264	echo "$algorithm|$digest"
265}
266
267ima_check()
268{
269	local test_file="$1"
270	local algorithm digest expected_digest line tmp
271
272	# need to read file to get updated $ASCII_MEASUREMENTS
273	cat $test_file > /dev/null
274
275	line="$(grep $test_file $ASCII_MEASUREMENTS | tail -1)"
276
277	if tmp=$(get_algorithm_digest "$line"); then
278		algorithm=$(echo "$tmp" | cut -d'|' -f1)
279		digest=$(echo "$tmp" | cut -d'|' -f2)
280	else
281		tst_res TBROK "failed to get algorithm/digest for '$test_file': $tmp"
282	fi
283
284	tst_res TINFO "computing digest for $algorithm algorithm"
285	expected_digest="$(compute_digest $algorithm $test_file)" || \
286		tst_brk TCONF "cannot compute digest for $algorithm algorithm"
287
288	if [ "$digest" = "$expected_digest" ]; then
289		tst_res TPASS "correct digest found"
290	else
291		tst_res TFAIL "digest not found"
292	fi
293}
294
295# check_evmctl REQUIRED_TPM_VERSION
296# return: 0: evmctl is new enough, 1: version older than required (or version < v0.9)
297check_evmctl()
298{
299	local required="$1"
300
301	local r1="$(echo $required | cut -d. -f1)"
302	local r2="$(echo $required | cut -d. -f2)"
303	local r3="$(echo $required | cut -d. -f3)"
304	[ -z "$r3" ] && r3=0
305
306	tst_is_int "$r1" || tst_brk TBROK "required major version not int ($v1)"
307	tst_is_int "$r2" || tst_brk TBROK "required minor version not int ($v2)"
308	tst_is_int "$r3" || tst_brk TBROK "required patch version not int ($v3)"
309
310	tst_check_cmds evmctl || return 1
311
312	local v="$(evmctl --version | cut -d' ' -f2)"
313	[ -z "$v" ] && return 1
314	tst_res TINFO "evmctl version: $v"
315
316	local v1="$(echo $v | cut -d. -f1)"
317	local v2="$(echo $v | cut -d. -f2)"
318	local v3="$(echo $v | cut -d. -f3)"
319	[ -z "$v3" ] && v3=0
320
321	if [ $v1 -lt $r1 ] || [ $v1 -eq $r1 -a $v2 -lt $r2 ] || \
322		[ $v1 -eq $r1 -a $v2 -eq $r2 -a $v3 -lt $r3 ]; then
323		return 1
324	fi
325	return 0
326}
327
328# require_evmctl REQUIRED_TPM_VERSION
329require_evmctl()
330{
331	local required="$1"
332
333	if ! check_evmctl $required; then
334		tst_brk TCONF "evmctl >= $required required"
335	fi
336}
337
338. tst_test.sh
339
340# loop device is needed to use only for tmpfs
341TMPDIR="${TMPDIR:-/tmp}"
342if tst_supported_fs -d $TMPDIR -s "tmpfs"; then
343	unset TST_MOUNT_DEVICE
344fi
345
346. tst_test.sh
347