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