• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/bin/sh
2# SPDX-License-Identifier: GPL-2.0-or-later
3# Copyright (c) 2021 Microsoft Corporation
4# Author: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
5#
6# Verify measurement of SELinux policy hash and state.
7#
8# Relevant kernel commits:
9# * fdd1ffe8a812 ("selinux: include a consumer of the new IMA critical data hook")
10# * 2554a48f4437 ("selinux: measure state and policy capabilities")
11
12TST_NEEDS_CMDS="awk cut grep tail"
13TST_CNT=2
14TST_SETUP="setup"
15TST_MIN_KVER="5.12"
16
17FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
18REQUIRED_POLICY="^measure.*$FUNC_CRITICAL_DATA"
19
20setup()
21{
22	SELINUX_DIR=$(tst_get_selinux_dir)
23	[ "$SELINUX_DIR" ] || tst_brk TCONF "SELinux is not enabled"
24
25	require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt
26}
27
28# Format of the measured SELinux state data.
29#
30# initialized=1;enforcing=0;checkreqprot=1;
31# network_peer_controls=1;open_perms=1;extended_socket_class=1;
32# always_check_network=0;cgroup_seclabel=1;nnp_nosuid_transition=1;
33# genfs_seclabel_symlinks=0;
34validate_policy_capabilities()
35{
36	local measured_cap measured_value expected_value
37	local inx=7
38
39	# Policy capabilities flags start from "network_peer_controls"
40	# in the measured SELinux state at offset 7 for 'awk'
41	while [ $inx -lt 20 ]; do
42		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
43		inx=$(($inx + 1))
44
45		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
46		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
47		if [ "$measured_value" != "$expected_value" ]; then
48			tst_res TFAIL "$measured_cap: expected: $expected_value, got: $digest"
49			return
50		fi
51
52		inx=$(($inx + 1))
53	done
54
55	tst_res TPASS "SELinux state measured correctly"
56}
57
58# Trigger measurement of SELinux constructs and verify that
59# the measured SELinux policy hash matches the hash of the policy
60# loaded in kernel memory for SELinux.
61test1()
62{
63	local policy_digest expected_policy_digest algorithm
64	local data_source_name="selinux"
65	local pattern="data_sources=[^[:space:]]*$data_source_name"
66	local tmp_file="$TST_TMPDIR/selinux_policy_tmp_file.txt"
67
68	tst_res TINFO "verifying SELinux policy hash measurement"
69
70	# Trigger a measurement by changing SELinux state
71	tst_update_selinux_state
72
73	# Verify SELinux policy hash is measured and then validate that
74	# the measured policy hash matches the hash of the policy currently
75	# in kernel memory for SELinux
76	line=$(grep -E "selinux-policy-hash" $ASCII_MEASUREMENTS | tail -1)
77	if [ -z "$line" ]; then
78		tst_res TFAIL "SELinux policy hash not measured"
79		return
80	fi
81
82	algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
83	policy_digest=$(echo "$line" | cut -d' ' -f6)
84
85	expected_policy_digest="$(compute_digest $algorithm $SELINUX_DIR/policy)" || \
86		tst_brk TCONF "cannot compute digest for $algorithm"
87
88	if [ "$policy_digest" != "$expected_policy_digest" ]; then
89		tst_res TFAIL "Digest mismatch: expected: $expected_policy_digest, got: $policy_digest"
90		return
91	fi
92
93	tst_res TPASS "SELinux policy hash measured correctly"
94}
95
96# Trigger measurement of SELinux constructs and verify that
97# the measured SELinux state matches the current SELinux
98# configuration.
99test2()
100{
101	local measured_data state_file="$TST_TMPDIR/selinux_state.txt"
102	local data_source_name="selinux"
103	local pattern="data_sources=[^[:space:]]*$data_source_name"
104	local tmp_file="$TST_TMPDIR/selinux_state_tmp_file.txt"
105	local digest expected_digest algorithm
106	local initialized_value
107	local enforced_value expected_enforced_value
108	local checkreqprot_value expected_checkreqprot_value
109
110	tst_res TINFO "verifying SELinux state measurement"
111
112	# Trigger a measurement by changing SELinux state
113	tst_update_selinux_state
114
115	# Verify SELinux state is measured and then validate the measured
116	# state matches that currently set for SELinux
117	line=$(grep -E "selinux-state" $ASCII_MEASUREMENTS | tail -1)
118	if [ -z "$line" ]; then
119		tst_res TFAIL "SELinux state not measured"
120		return
121	fi
122
123	digest=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f2)
124	algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
125
126	echo "$line" | cut -d' ' -f6 | tst_hexdump -d > $state_file
127
128	expected_digest="$(compute_digest $algorithm $state_file)" || \
129	tst_brk TCONF "cannot compute digest for $algorithm"
130
131	if [ "$digest" != "$expected_digest" ]; then
132		tst_res TFAIL "digest mismatch: expected: $expected_digest, got: $digest"
133		return
134	fi
135
136	# SELinux state is measured as the following string
137	#   initialized=1;enforcing=0;checkreqprot=1;
138	# Value of 0 indicates the state is ON, and 1 indicates OFF
139	#
140	# enforce and checkreqprot measurement can be verified by
141	# comparing the value of the file "enforce" and "checkreqprot"
142	# respectively in the SELinux directory.
143	# "initialized" is an internal state and should be set to 1
144	# if enforce and checkreqprot are measured correctly.
145	measured_data=$(cat $state_file)
146	enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}')
147	expected_enforced_value=$(cat $SELINUX_DIR/enforce)
148	if [ "$expected_enforced_value" != "$enforced_value" ]; then
149		tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value"
150		return
151	fi
152
153	checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}')
154	expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot)
155	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ]; then
156		tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value"
157		return
158	fi
159
160	initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}')
161	if [ "$initialized_value" != "1" ]; then
162		tst_res TFAIL "initialized: expected 1, got: $initialized_value"
163		return
164	fi
165
166	validate_policy_capabilities $measured_data
167}
168
169. ima_setup.sh
170tst_run
171