• 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
76check_ima_policy_content()
77{
78	local pattern="$1"
79	local grep_params="${2--q}"
80
81	check_policy_readable || return 1
82	grep $grep_params "$pattern" $IMA_POLICY
83}
84
85require_ima_policy_content()
86{
87	local pattern="$1"
88	local grep_params="${2--q}"
89
90	require_policy_readable
91	if ! grep $grep_params "$pattern" $IMA_POLICY; then
92		tst_brk TCONF "IMA policy does not specify '$pattern'"
93	fi
94}
95
96check_ima_policy_cmdline()
97{
98	local policy="$1"
99	local i
100
101	grep -q "ima_$policy" /proc/cmdline && return
102	for i in $(cat /proc/cmdline); do
103		if echo "$i" | grep -q '^ima_policy='; then
104			echo "$i" | grep -q -e "|[ ]*$policy" -e "$policy[ ]*|" -e "=$policy" && return 0
105		fi
106	done
107	return 1
108}
109
110require_ima_policy_cmdline()
111{
112	local policy="$1"
113
114	check_ima_policy_cmdline $policy || \
115		tst_brk TCONF "IMA measurement tests require builtin IMA $policy policy (e.g. ima_policy=$policy kernel parameter)"
116}
117
118mount_helper()
119{
120	local type="$1"
121	local default_dir="$2"
122	local dir
123
124	dir="$(grep ^$type /proc/mounts | cut -d ' ' -f2 | head -1)"
125	[ -n "$dir" ] && { echo "$dir"; return; }
126
127	if ! mkdir -p $default_dir; then
128		tst_brk TBROK "failed to create $default_dir"
129	fi
130	if ! mount -t $type $type $default_dir; then
131		tst_brk TBROK "failed to mount $type"
132	fi
133	UMOUNT="$default_dir $UMOUNT"
134	echo $default_dir
135}
136
137mount_loop_device()
138{
139	local ret
140
141	tst_mkfs
142	tst_mount
143	cd $TST_MNTPOINT
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_NEEDS_DEVICE" = 1 ]; then
179		tst_res TINFO "\$TMPDIR is on tmpfs => run on loop device"
180		mount_loop_device
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	if [ "$TST_NEEDS_DEVICE" = 1 ]; then
197		cd $TST_TMPDIR
198		tst_umount
199	fi
200}
201
202set_digest_index()
203{
204	DIGEST_INDEX=
205
206	local template="$(tail -1 $ASCII_MEASUREMENTS | cut -d' ' -f 3)"
207	local i word
208
209	# parse digest index
210	# https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use
211	case "$template" in
212	ima|ima-ng|ima-sig) DIGEST_INDEX=4 ;;
213	*)
214		# using ima_template_fmt kernel parameter
215		local IFS="|"
216		i=4
217		for word in $template; do
218			if [ "$word" = 'd' -o "$word" = 'd-ng' ]; then
219				DIGEST_INDEX=$i
220				break
221			fi
222			i=$((i+1))
223		done
224	esac
225
226	[ -z "$DIGEST_INDEX" ] && tst_brk TCONF \
227		"Cannot find digest index (template: '$template')"
228}
229
230get_algorithm_digest()
231{
232	local line="$1"
233	local delimiter=':'
234	local algorithm digest
235
236	if [ -z "$line" ]; then
237		echo "measurement record not found"
238		return 1
239	fi
240
241	[ -z "$DIGEST_INDEX" ] && set_digest_index
242	digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX)
243	if [ -z "$digest" ]; then
244		echo "digest not found (index: $DIGEST_INDEX, line: '$line')"
245		return 1
246	fi
247
248	if [ "${digest#*$delimiter}" != "$digest" ]; then
249		algorithm=$(echo "$digest" | cut -d $delimiter -f 1)
250		digest=$(echo "$digest" | cut -d $delimiter -f 2)
251	else
252		case "${#digest}" in
253		32) algorithm="md5" ;;
254		40) algorithm="sha1" ;;
255		*)
256			echo "algorithm must be either md5 or sha1 (digest: '$digest')"
257			return 1 ;;
258		esac
259	fi
260	if [ -z "$algorithm" ]; then
261		echo "algorithm not found"
262		return 1
263	fi
264	if [ -z "$digest" ]; then
265		echo "digest not found"
266		return 1
267	fi
268
269	echo "$algorithm|$digest"
270}
271
272# check_evmctl REQUIRED_TPM_VERSION
273# return: 0: evmctl is new enough, 1: version older than required (or version < v0.9)
274check_evmctl()
275{
276	local required="$1"
277
278	local r1="$(echo $required | cut -d. -f1)"
279	local r2="$(echo $required | cut -d. -f2)"
280	local r3="$(echo $required | cut -d. -f3)"
281	[ -z "$r3" ] && r3=0
282
283	tst_is_int "$r1" || tst_brk TBROK "required major version not int ($v1)"
284	tst_is_int "$r2" || tst_brk TBROK "required minor version not int ($v2)"
285	tst_is_int "$r3" || tst_brk TBROK "required patch version not int ($v3)"
286
287	tst_check_cmds evmctl || return 1
288
289	local v="$(evmctl --version | cut -d' ' -f2)"
290	[ -z "$v" ] && return 1
291	tst_res TINFO "evmctl version: $v"
292
293	local v1="$(echo $v | cut -d. -f1)"
294	local v2="$(echo $v | cut -d. -f2)"
295	local v3="$(echo $v | cut -d. -f3)"
296	[ -z "$v3" ] && v3=0
297
298	if [ $v1 -lt $r1 ] || [ $v1 -eq $r1 -a $v2 -lt $r2 ] || \
299		[ $v1 -eq $r1 -a $v2 -eq $r2 -a $v3 -lt $r3 ]; then
300		return 1
301	fi
302	return 0
303}
304
305# require_evmctl REQUIRED_TPM_VERSION
306require_evmctl()
307{
308	local required="$1"
309
310	if ! check_evmctl $required; then
311		tst_brk TCONF "evmctl >= $required required"
312	fi
313}
314
315# loop device is needed to use only for tmpfs
316TMPDIR="${TMPDIR:-/tmp}"
317if [ "$(df -T $TMPDIR | tail -1 | awk '{print $2}')" != "tmpfs" -a -n "$TST_NEEDS_DEVICE" ]; then
318	unset TST_NEEDS_DEVICE
319fi
320