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