• 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
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 "test requires builtin IMA $policy policy (e.g. ima_policy=$policy kernel command line 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	if [ -z "$DIGEST_INDEX" ]; then
222		tst_res TWARN "Cannot find digest index (template: '$template')"
223		return 1
224	fi
225}
226
227get_algorithm_digest()
228{
229	local line="$1"
230	local delimiter=':'
231	local algorithm digest
232
233	if [ -z "$line" ]; then
234		echo "measurement record not found"
235		return 1
236	fi
237
238	if [ -z "$DIGEST_INDEX" ]; then
239		set_digest_index
240	fi
241	if [ -z "$DIGEST_INDEX" ]; then
242		return 1
243	fi
244
245	digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX)
246	if [ -z "$digest" ]; then
247		echo "digest not found (index: $DIGEST_INDEX, line: '$line')"
248		return 1
249	fi
250
251	if [ "${digest#*$delimiter}" != "$digest" ]; then
252		algorithm=$(echo "$digest" | cut -d $delimiter -f 1)
253		digest=$(echo "$digest" | cut -d $delimiter -f 2)
254	else
255		case "${#digest}" in
256		32) algorithm="md5" ;;
257		40) algorithm="sha1" ;;
258		*)
259			echo "algorithm must be either md5 or sha1 (digest: '$digest')"
260			return 1 ;;
261		esac
262	fi
263	if [ -z "$algorithm" ]; then
264		echo "algorithm not found"
265		return 1
266	fi
267	if [ -z "$digest" ]; then
268		echo "digest not found"
269		return 1
270	fi
271
272	echo "$algorithm|$digest"
273}
274
275ima_check()
276{
277	local test_file="$1"
278	local algorithm digest expected_digest line
279
280	# need to read file to get updated $ASCII_MEASUREMENTS
281	cat $test_file > /dev/null
282
283	line="$(grep $test_file $ASCII_MEASUREMENTS | tail -1)"
284
285	if get_algorithm_digest "$line" > tmp; then
286		algorithm=$(cat tmp | cut -d'|' -f1)
287		digest=$(cat tmp | cut -d'|' -f2)
288	else
289		tst_brk TBROK "failed to get algorithm/digest for '$test_file'"
290	fi
291
292	tst_res TINFO "computing digest for $algorithm algorithm"
293	expected_digest="$(compute_digest $algorithm $test_file)" || \
294		tst_brk TCONF "cannot compute digest for $algorithm algorithm"
295
296	if [ "$digest" = "$expected_digest" ]; then
297		tst_res TPASS "correct digest found"
298	else
299		tst_res TFAIL "digest not found"
300	fi
301}
302
303# check_evmctl REQUIRED_TPM_VERSION
304# return: 0: evmctl is new enough, 1: version older than required (or version < v0.9)
305check_evmctl()
306{
307	local required="$1"
308
309	local r1="$(echo $required | cut -d. -f1)"
310	local r2="$(echo $required | cut -d. -f2)"
311	local r3="$(echo $required | cut -d. -f3)"
312	[ -z "$r3" ] && r3=0
313
314	tst_is_int "$r1" || tst_brk TBROK "required major version not int ($v1)"
315	tst_is_int "$r2" || tst_brk TBROK "required minor version not int ($v2)"
316	tst_is_int "$r3" || tst_brk TBROK "required patch version not int ($v3)"
317
318	tst_check_cmds evmctl || return 1
319
320	local v="$(evmctl --version | cut -d' ' -f2)"
321	[ -z "$v" ] && return 1
322	tst_res TINFO "evmctl version: $v"
323
324	local v1="$(echo $v | cut -d. -f1)"
325	local v2="$(echo $v | cut -d. -f2)"
326	local v3="$(echo $v | cut -d. -f3)"
327	[ -z "$v3" ] && v3=0
328
329	if [ $v1 -lt $r1 ] || [ $v1 -eq $r1 -a $v2 -lt $r2 ] || \
330		[ $v1 -eq $r1 -a $v2 -eq $r2 -a $v3 -lt $r3 ]; then
331		return 1
332	fi
333	return 0
334}
335
336# require_evmctl REQUIRED_TPM_VERSION
337require_evmctl()
338{
339	local required="$1"
340
341	if ! check_evmctl $required; then
342		tst_brk TCONF "evmctl >= $required required"
343	fi
344}
345
346. tst_test.sh
347
348# loop device is needed to use only for tmpfs
349TMPDIR="${TMPDIR:-/tmp}"
350if tst_supported_fs -d $TMPDIR -s "tmpfs"; then
351	unset TST_MOUNT_DEVICE
352fi
353
354. tst_test.sh
355