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