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