1#!/bin/sh 2# SPDX-License-Identifier: GPL-2.0-or-later 3# Copyright (c) 2009 IBM Corporation 4# Copyright (c) 2018-2020 Petr Vorel <pvorel@suse.cz> 5# Author: Mimi Zohar <zohar@linux.ibm.com> 6 7TST_TESTFUNC="test" 8TST_SETUP_CALLER="$TST_SETUP" 9TST_SETUP="ima_setup" 10TST_CLEANUP_CALLER="$TST_CLEANUP" 11TST_CLEANUP="ima_cleanup" 12TST_NEEDS_ROOT=1 13 14# TST_NEEDS_DEVICE can be unset, therefore specify explicitly 15TST_NEEDS_TMPDIR=1 16 17. tst_test.sh 18 19SYSFS="/sys" 20UMOUNT= 21TST_FS_TYPE="ext3" 22 23# TODO: find support for rmd128 rmd256 rmd320 wp256 wp384 tgr128 tgr160 24compute_digest() 25{ 26 local algorithm="$1" 27 local file="$2" 28 local digest 29 30 digest="$(${algorithm}sum $file 2>/dev/null | cut -f1 -d ' ')" 31 if [ -n "$digest" ]; then 32 echo "$digest" 33 return 0 34 fi 35 36 digest="$(openssl $algorithm $file 2>/dev/null | cut -f2 -d ' ')" 37 if [ -n "$digest" ]; then 38 echo "$digest" 39 return 0 40 fi 41 42 # uncommon ciphers 43 local arg="$algorithm" 44 case "$algorithm" in 45 tgr192) arg="tiger" ;; 46 wp512) arg="whirlpool" ;; 47 esac 48 49 digest="$(rdigest --$arg $file 2>/dev/null | cut -f1 -d ' ')" 50 if [ -n "$digest" ]; then 51 echo "$digest" 52 return 0 53 fi 54 return 1 55} 56 57check_policy_readable() 58{ 59 if [ ! -f $IMA_POLICY ]; then 60 tst_res TINFO "missing $IMA_POLICY (reboot or CONFIG_IMA_WRITE_POLICY=y required)" 61 return 1 62 fi 63 cat $IMA_POLICY > /dev/null 2>/dev/null 64} 65 66require_policy_readable() 67{ 68 if [ ! -f $IMA_POLICY ]; then 69 tst_brk TCONF "missing $IMA_POLICY (reboot or CONFIG_IMA_WRITE_POLICY=y required)" 70 fi 71 if ! check_policy_readable; then 72 tst_brk TCONF "cannot read IMA policy (CONFIG_IMA_READ_POLICY=y required)" 73 fi 74} 75 76require_policy_writable() 77{ 78 local err="IMA policy already loaded and kernel not configured to enable multiple writes to it (need CONFIG_IMA_WRITE_POLICY=y)" 79 80 [ -f $IMA_POLICY ] || tst_brk TCONF "$err" 81 # CONFIG_IMA_READ_POLICY 82 echo "" 2> log > $IMA_POLICY 83 grep -q "Device or resource busy" log && tst_brk TCONF "$err" 84} 85 86check_ima_policy_content() 87{ 88 local pattern="$1" 89 local grep_params="${2--q}" 90 91 check_policy_readable || return 1 92 grep $grep_params "$pattern" $IMA_POLICY 93} 94 95require_ima_policy_content() 96{ 97 local pattern="$1" 98 local grep_params="${2--q}" 99 100 require_policy_readable 101 if ! grep $grep_params "$pattern" $IMA_POLICY; then 102 tst_brk TCONF "IMA policy does not specify '$pattern'" 103 fi 104} 105 106check_ima_policy_cmdline() 107{ 108 local policy="$1" 109 local i 110 111 grep -q "ima_$policy" /proc/cmdline && return 112 for i in $(cat /proc/cmdline); do 113 if echo "$i" | grep -q '^ima_policy='; then 114 echo "$i" | grep -q -e "|[ ]*$policy" -e "$policy[ ]*|" -e "=$policy" && return 0 115 fi 116 done 117 return 1 118} 119 120require_ima_policy_cmdline() 121{ 122 local policy="$1" 123 124 check_ima_policy_cmdline $policy || \ 125 tst_brk TCONF "IMA measurement tests require builtin IMA $policy policy (e.g. ima_policy=$policy kernel parameter)" 126} 127 128mount_helper() 129{ 130 local type="$1" 131 local default_dir="$2" 132 local dir 133 134 dir="$(grep ^$type /proc/mounts | cut -d ' ' -f2 | head -1)" 135 [ -n "$dir" ] && { echo "$dir"; return; } 136 137 if ! mkdir -p $default_dir; then 138 tst_brk TBROK "failed to create $default_dir" 139 fi 140 if ! mount -t $type $type $default_dir; then 141 tst_brk TBROK "failed to mount $type" 142 fi 143 UMOUNT="$default_dir $UMOUNT" 144 echo $default_dir 145} 146 147mount_loop_device() 148{ 149 local ret 150 151 tst_mkfs 152 tst_mount 153 cd $TST_MNTPOINT 154} 155 156print_ima_config() 157{ 158 local config="${KCONFIG_PATH:-/boot/config-$(uname -r)}" 159 local i 160 161 if [ -r "$config" ]; then 162 tst_res TINFO "IMA kernel config:" 163 for i in $(grep ^CONFIG_IMA $config); do 164 tst_res TINFO "$i" 165 done 166 fi 167 168 tst_res TINFO "/proc/cmdline: $(cat /proc/cmdline)" 169} 170 171ima_setup() 172{ 173 SECURITYFS="$(mount_helper securityfs $SYSFS/kernel/security)" 174 175 IMA_DIR="$SECURITYFS/ima" 176 [ -d "$IMA_DIR" ] || tst_brk TCONF "IMA not enabled in kernel" 177 ASCII_MEASUREMENTS="$IMA_DIR/ascii_runtime_measurements" 178 BINARY_MEASUREMENTS="$IMA_DIR/binary_runtime_measurements" 179 IMA_POLICY="$IMA_DIR/policy" 180 181 # hack to support running tests locally from ima/tests directory 182 if [ ! -d "$TST_DATAROOT" ]; then 183 TST_DATAROOT="$LTPROOT/../datafiles/$TST_ID/" 184 fi 185 186 print_ima_config 187 188 if [ "$TST_NEEDS_DEVICE" = 1 ]; then 189 tst_res TINFO "\$TMPDIR is on tmpfs => run on loop device" 190 mount_loop_device 191 fi 192 193 [ -n "$TST_SETUP_CALLER" ] && $TST_SETUP_CALLER 194} 195 196ima_cleanup() 197{ 198 local dir 199 200 [ -n "$TST_CLEANUP_CALLER" ] && $TST_CLEANUP_CALLER 201 202 for dir in $UMOUNT; do 203 umount $dir 204 done 205 206 if [ "$TST_NEEDS_DEVICE" = 1 ]; then 207 cd $TST_TMPDIR 208 tst_umount 209 fi 210} 211 212set_digest_index() 213{ 214 DIGEST_INDEX= 215 216 local template="$(tail -1 $ASCII_MEASUREMENTS | cut -d' ' -f 3)" 217 local i word 218 219 # parse digest index 220 # https://www.kernel.org/doc/html/latest/security/IMA-templates.html#use 221 case "$template" in 222 ima|ima-ng|ima-sig) DIGEST_INDEX=4 ;; 223 *) 224 # using ima_template_fmt kernel parameter 225 local IFS="|" 226 i=4 227 for word in $template; do 228 if [ "$word" = 'd' -o "$word" = 'd-ng' ]; then 229 DIGEST_INDEX=$i 230 break 231 fi 232 i=$((i+1)) 233 done 234 esac 235 236 [ -z "$DIGEST_INDEX" ] && tst_brk TCONF \ 237 "Cannot find digest index (template: '$template')" 238} 239 240get_algorithm_digest() 241{ 242 local line="$1" 243 local delimiter=':' 244 local algorithm digest 245 246 if [ -z "$line" ]; then 247 echo "measurement record not found" 248 return 1 249 fi 250 251 [ -z "$DIGEST_INDEX" ] && set_digest_index 252 digest=$(echo "$line" | cut -d' ' -f $DIGEST_INDEX) 253 if [ -z "$digest" ]; then 254 echo "digest not found (index: $DIGEST_INDEX, line: '$line')" 255 return 1 256 fi 257 258 if [ "${digest#*$delimiter}" != "$digest" ]; then 259 algorithm=$(echo "$digest" | cut -d $delimiter -f 1) 260 digest=$(echo "$digest" | cut -d $delimiter -f 2) 261 else 262 case "${#digest}" in 263 32) algorithm="md5" ;; 264 40) algorithm="sha1" ;; 265 *) 266 echo "algorithm must be either md5 or sha1 (digest: '$digest')" 267 return 1 ;; 268 esac 269 fi 270 if [ -z "$algorithm" ]; then 271 echo "algorithm not found" 272 return 1 273 fi 274 if [ -z "$digest" ]; then 275 echo "digest not found" 276 return 1 277 fi 278 279 echo "$algorithm|$digest" 280} 281 282ima_check() 283{ 284 local test_file="$1" 285 local algorithm digest expected_digest line tmp 286 287 # need to read file to get updated $ASCII_MEASUREMENTS 288 cat $test_file > /dev/null 289 290 line="$(grep $test_file $ASCII_MEASUREMENTS | tail -1)" 291 292 if tmp=$(get_algorithm_digest "$line"); then 293 algorithm=$(echo "$tmp" | cut -d'|' -f1) 294 digest=$(echo "$tmp" | cut -d'|' -f2) 295 else 296 tst_res TBROK "failed to get algorithm/digest for '$test_file': $tmp" 297 fi 298 299 tst_res TINFO "computing digest for $algorithm algorithm" 300 expected_digest="$(compute_digest $algorithm $test_file)" || \ 301 tst_brk TCONF "cannot compute digest for $algorithm algorithm" 302 303 if [ "$digest" = "$expected_digest" ]; then 304 tst_res TPASS "correct digest found" 305 else 306 tst_res TFAIL "digest not found" 307 fi 308} 309 310# check_evmctl REQUIRED_TPM_VERSION 311# return: 0: evmctl is new enough, 1: version older than required (or version < v0.9) 312check_evmctl() 313{ 314 local required="$1" 315 316 local r1="$(echo $required | cut -d. -f1)" 317 local r2="$(echo $required | cut -d. -f2)" 318 local r3="$(echo $required | cut -d. -f3)" 319 [ -z "$r3" ] && r3=0 320 321 tst_is_int "$r1" || tst_brk TBROK "required major version not int ($v1)" 322 tst_is_int "$r2" || tst_brk TBROK "required minor version not int ($v2)" 323 tst_is_int "$r3" || tst_brk TBROK "required patch version not int ($v3)" 324 325 tst_check_cmds evmctl || return 1 326 327 local v="$(evmctl --version | cut -d' ' -f2)" 328 [ -z "$v" ] && return 1 329 tst_res TINFO "evmctl version: $v" 330 331 local v1="$(echo $v | cut -d. -f1)" 332 local v2="$(echo $v | cut -d. -f2)" 333 local v3="$(echo $v | cut -d. -f3)" 334 [ -z "$v3" ] && v3=0 335 336 if [ $v1 -lt $r1 ] || [ $v1 -eq $r1 -a $v2 -lt $r2 ] || \ 337 [ $v1 -eq $r1 -a $v2 -eq $r2 -a $v3 -lt $r3 ]; then 338 return 1 339 fi 340 return 0 341} 342 343# require_evmctl REQUIRED_TPM_VERSION 344require_evmctl() 345{ 346 local required="$1" 347 348 if ! check_evmctl $required; then 349 tst_brk TCONF "evmctl >= $required required" 350 fi 351} 352 353# loop device is needed to use only for tmpfs 354TMPDIR="${TMPDIR:-/tmp}" 355if [ "$(df -T $TMPDIR | tail -1 | awk '{print $2}')" != "tmpfs" -a -n "$TST_NEEDS_DEVICE" ]; then 356 unset TST_NEEDS_DEVICE 357fi 358