1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-or-later
3# Copyright (c) 2009 IBM Corporation
4# Copyright (c) 2018-2019 Petr Vorel <pvorel@suse.cz>
5# Author: Mimi Zohar <zohar@linux.ibm.com>
6#
7# Verify that measurements are added to the measurement list based on policy.
8
9TST_NEEDS_CMDS="awk cut"
10TST_SETUP="setup"
11TST_CNT=3
12TST_NEEDS_DEVICE=1
13
14. ima_setup.sh
15
16setup()
17{
18	check_ima_policy "tcb"
19
20	TEST_FILE="$PWD/test.txt"
21	POLICY="$IMA_DIR/policy"
22	[ -f "$POLICY" ] || tst_res TINFO "not using default policy"
23	DIGEST_INDEX=
24
25	local template="$(tail -1 $ASCII_MEASUREMENTS | cut -d' ' -f 3)"
26	local i
27
28	# parse digest index
29	# https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use
30	case "$template" in
31	ima|ima-ng|ima-sig) DIGEST_INDEX=4 ;;
32	*)
33		# using ima_template_fmt kernel parameter
34		local IFS="|"
35		i=4
36		for word in $template; do
37			if [ "$word" = 'd' -o "$word" = 'd-ng' ]; then
38				DIGEST_INDEX=$i
39				break
40			fi
41			i=$((i+1))
42		done
43	esac
44
45	[ -z "$DIGEST_INDEX" ] && tst_brk TCONF \
46		"Cannot find digest index (template: '$template')"
47}
48
49# TODO: find support for rmd128 rmd256 rmd320 wp256 wp384 tgr128 tgr160
50compute_digest()
51{
52	local algorithm="$1"
53	local file="$2"
54	local digest
55
56	digest="$(${algorithm}sum $file 2>/dev/null | cut -f1 -d ' ')"
57	if [ -n "$digest" ]; then
58		echo "$digest"
59		return 0
60	fi
61
62	digest="$(openssl $algorithm $file 2>/dev/null | cut -f2 -d ' ')"
63	if [ -n "$digest" ]; then
64		echo "$digest"
65		return 0
66	fi
67
68	# uncommon ciphers
69	local arg="$algorithm"
70	case "$algorithm" in
71	tgr192) arg="tiger" ;;
72	wp512) arg="whirlpool" ;;
73	esac
74
75	digest="$(rdigest --$arg $file 2>/dev/null | cut -f1 -d ' ')"
76	if [ -n "$digest" ]; then
77		echo "$digest"
78		return 0
79	fi
80	return 1
81}
82
83ima_check()
84{
85	local delimiter=':'
86	local algorithm digest expected_digest line
87
88	# need to read file to get updated $ASCII_MEASUREMENTS
89	cat $TEST_FILE > /dev/null
90
91	line="$(grep $TEST_FILE $ASCII_MEASUREMENTS | tail -1)"
92	if [ -z "$line" ]; then
93		tst_res TFAIL "cannot find measurement record for '$TEST_FILE'"
94		return
95	fi
96	tst_res TINFO "measurement record: '$line'"
97
98	digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX)
99	if [ -z "$digest" ]; then
100		tst_res TFAIL "cannot find digest (index: $DIGEST_INDEX)"
101		return
102	fi
103
104	if [ "${digest#*$delimiter}" != "$digest" ]; then
105		algorithm=$(echo "$digest" | cut -d $delimiter -f 1)
106		digest=$(echo "$digest" | cut -d $delimiter -f 2)
107	else
108		case "${#digest}" in
109		32) algorithm="md5" ;;
110		40) algorithm="sha1" ;;
111		*)
112			tst_res TFAIL "algorithm must be either md5 or sha1 (digest: '$digest')"
113			return ;;
114		esac
115	fi
116	if [ -z "$algorithm" ]; then
117		tst_res TFAIL "cannot find algorithm"
118		return
119	fi
120	if [ -z "$digest" ]; then
121		tst_res TFAIL "cannot find digest"
122		return
123	fi
124
125	tst_res TINFO "computing digest for $algorithm algorithm"
126	expected_digest="$(compute_digest $algorithm $TEST_FILE)" || \
127		tst_brk TCONF "cannot compute digest for $algorithm algorithm"
128
129	if [ "$digest" = "$expected_digest" ]; then
130		tst_res TPASS "correct digest found"
131	else
132		tst_res TFAIL "digest not found"
133	fi
134}
135
136check_iversion_support()
137{
138	local device mount fs
139
140	tst_kvcmp -ge "4.16" && return 0
141
142	device="$(df . | sed -e 1d | cut -f1 -d ' ')"
143	mount="$(grep $device /proc/mounts | head -1)"
144	fs="$(echo $mount | awk '{print $3'})"
145
146	case "$fs" in
147	ext[2-4])
148		if ! echo "$mount" | grep -q -w "i_version"; then
149			tst_res TCONF "device '$device' is not mounted with iversion, please mount it with 'mount $device -o remount,iversion'"
150			return 1
151		fi
152		;;
153	xfs)
154		if dmesg | grep -q "XFS.*Mounting V[1-4] Filesystem"; then
155			tst_res TCONF "XFS Filesystem >= V5 required for iversion support"
156			return 1
157		fi
158		;;
159	'')
160		tst_res TWARN "could not find mount info for device '$device'"
161		;;
162	esac
163
164	return 0
165}
166
167test1()
168{
169	tst_res TINFO "verify adding record to the IMA measurement list"
170	ROD echo "$(date) this is a test file" \> $TEST_FILE
171	ima_check
172}
173
174test2()
175{
176
177	tst_res TINFO "verify updating record in the IMA measurement list"
178	check_iversion_support || return
179	ROD echo "$(date) modified file" \> $TEST_FILE
180	ima_check
181}
182
183test3()
184{
185	local user="nobody"
186	local dir="$PWD/user"
187	local file="$dir/test.txt"
188
189	# Default policy does not measure user files
190	tst_res TINFO "verify not measuring user files"
191	tst_check_cmds sudo
192
193	if ! id $user >/dev/null 2>/dev/null; then
194		tst_res TCONF "missing system user $user (wrong installation)"
195		return
196	fi
197
198	mkdir -m 0700 $dir
199	chown $user $dir
200	cd $dir
201	# need to read file to get updated $ASCII_MEASUREMENTS
202	sudo -n -u $user sh -c "echo $(date) user file > $file; cat $file > /dev/null"
203	cd ..
204
205	EXPECT_FAIL "grep $file $ASCII_MEASUREMENTS"
206}
207
208tst_run
209