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