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# 7# Verify the boot and PCR aggregates. 8 9TST_CNT=2 10TST_NEEDS_CMDS="awk cut tail" 11TST_SETUP="setup" 12 13. ima_setup.sh 14 15EVMCTL_REQUIRED='1.3.1' 16ERRMSG_EVMCTL="=> install evmctl >= $EVMCTL_REQUIRED" 17ERRMSG_TPM="TPM hardware support not enabled in kernel or no TPM chip found" 18 19setup() 20{ 21 local config="${KCONFIG_PATH:-/boot/config-$(uname -r)}" 22 local line tmp 23 24 read line < $ASCII_MEASUREMENTS 25 if tmp=$(get_algorithm_digest "$line"); then 26 ALGORITHM=$(echo "$tmp" | cut -d'|' -f1) 27 DIGEST=$(echo "$tmp" | cut -d'|' -f2) 28 else 29 tst_brk TBROK "failed to get algorithm/digest: $tmp" 30 fi 31 tst_res TINFO "used algorithm: $ALGORITHM" 32 33 TPM_VERSION="$(get_tpm_version)" 34 if [ -z "$TPM_VERSION" ]; then 35 tst_res TINFO "$ERRMSG_TPM, testing TPM-bypass" 36 else 37 tst_res TINFO "TPM major version: $TPM_VERSION" 38 fi 39 40 if ! check_evmctl $EVMCTL_REQUIRED; then 41 if [ "$ALGORITHM" != "sha1" ]; then 42 tst_brk TCONF "algorithm not sha1 ($ALGORITHM) $ERRMSG_EVMCTL" 43 fi 44 MISSING_EVMCTL=1 45 fi 46 47 if [ -r "$config" ]; then 48 tst_res TINFO "TPM kernel config:" 49 for i in $(grep -e ^CONFIG_.*_TPM -e ^CONFIG_TCG $config); do 50 tst_res TINFO "$i" 51 done 52 fi 53} 54 55# prints major version: 1: TPM 1.2, 2: TPM 2.0 56# or nothing on TPM-bypass (no TPM device) 57# WARNING: Detecting TPM 2.0 can fail due kernel not exporting TPM 2.0 files. 58get_tpm_version() 59{ 60 if [ -f /sys/class/tpm/tpm0/tpm_version_major ]; then 61 cat /sys/class/tpm/tpm0/tpm_version_major 62 return 63 fi 64 65 if [ -f /sys/class/tpm/tpm0/device/caps -o \ 66 -f /sys/class/misc/tpm0/device/caps ]; then 67 echo 1 68 return 69 fi 70 71 if [ -c /dev/tpmrm0 -a -c /dev/tpm0 ]; then 72 echo 2 73 return 74 fi 75 76 if [ ! -d /sys/class/tpm/tpm0/ -a ! -d /sys/class/misc/tpm0/ ]; then 77 return 78 fi 79 80 tst_require_cmds dmesg 81 if dmesg | grep -q 'activating TPM-bypass'; then 82 return 83 elif dmesg | grep -q '1\.2 TPM (device-id'; then 84 echo 1 85 return 86 elif dmesg | grep -q '2\.0 TPM (device-id'; then 87 echo 2 88 return 89 fi 90} 91 92read_pcr_tpm1() 93{ 94 local pcrs_path="/sys/class/tpm/tpm0/device/pcrs" 95 local evmctl_required="1.1" 96 local hash pcr 97 98 if [ ! -f "$pcrs_path" ]; then 99 pcrs_path="/sys/class/misc/tpm0/device/pcrs" 100 elif ! check_evmctl $evmctl_required; then 101 echo "evmctl >= $evmctl_required required" 102 return 32 103 fi 104 105 if [ ! -f "$pcrs_path" ]; then 106 echo "missing PCR file $pcrs_path ($ERRMSG_TPM)" 107 return 32 108 fi 109 110 while read line; do 111 pcr="$(echo $line | cut -d':' -f1)" 112 hash="$(echo $line | cut -d':' -f2 | awk '{ gsub (" ", "", $0); print tolower($0) }')" 113 echo "$pcr: $hash" 114 done < $pcrs_path 115 116 return 0 117} 118 119# NOTE: TPM 1.2 would require to use tss1pcrread which is not fully adopted 120# by distros yet. 121read_pcr_tpm2() 122{ 123 local pcrmax=23 124 local pcrread="tsspcrread -halg $ALGORITHM" 125 local i pcr 126 127 if ! tst_cmd_available tsspcrread; then 128 echo "tsspcrread not found" 129 return 32 130 fi 131 132 for i in $(seq 0 $pcrmax); do 133 pcr=$($pcrread -ha "$i" -ns) 134 if [ $? -ne 0 ]; then 135 echo "tsspcrread failed: $pcr" 136 return 1 137 fi 138 printf "PCR-%02d: %s\n" $i "$pcr" 139 done 140 141 return 0 142} 143 144get_pcr10_aggregate() 145{ 146 local cmd="evmctl -vv ima_measurement $BINARY_MEASUREMENTS" 147 local msg="$ERRMSG_EVMCTL" 148 local res=TCONF 149 local pcr ret 150 151 if [ -z "$MISSING_EVMCTL" ]; then 152 msg= 153 res=TFAIL 154 fi 155 156 $cmd > hash.txt 2>&1 157 ret=$? 158 if [ $ret -ne 0 -a -z "$MISSING_EVMCTL" ]; then 159 tst_res TFAIL "evmctl failed, trying with --ignore-violations" 160 cmd="$cmd --ignore-violations" 161 $cmd > hash.txt 2>&1 162 ret=$? 163 elif [ $ret -ne 0 -a "$MISSING_EVMCTL" = 1 ]; then 164 tst_brk TFAIL "evmctl failed $msg" 165 fi 166 167 [ $ret -ne 0 ] && tst_res TWARN "evmctl failed, trying to continue $msg" 168 169 pcr=$(grep -E "^($ALGORITHM: )*PCRAgg(.*10)*:" hash.txt | tail -1 \ 170 | awk '{print $NF}') 171 172 if [ -z "$pcr" ]; then 173 tst_res $res "failed to find aggregate PCR-10 $msg" 174 tst_res TINFO "hash file:" 175 cat hash.txt >&2 176 return 177 fi 178 179 echo "$pcr" 180} 181 182test1_tpm_bypass_mode() 183{ 184 local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}') 185 186 if [ "$DIGEST" = "$zero" ]; then 187 tst_res TPASS "bios boot aggregate is $zero" 188 else 189 tst_res TFAIL "bios boot aggregate is not $zero ($DIGEST), kernel didn't export TPM 2.0 files for TPM device?" 190 return 1 191 fi 192} 193 194test1_hw_tpm() 195{ 196 local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements" 197 local cmd="evmctl ima_boot_aggregate -v" 198 local boot_aggregate 199 200 if [ -z "$TPM_VERSION" ]; then 201 tst_res TWARN "TPM-bypass failed, trying to test TPM device (unknown TPM version)" 202 MAYBE_TPM2=1 203 fi 204 205 if [ "$MISSING_EVMCTL" = 1 ]; then 206 if [ ! -f "$tpm_bios" ]; then 207 tst_res TCONF "missing $tpm_bios $ERRMSG_EVMCTL" 208 return 209 fi 210 tst_check_cmds ima_boot_aggregate || return 211 212 cmd="ima_boot_aggregate -f $tpm_bios" 213 214 # TCONF: libcrypto and openssl development packages required 215 $cmd 216 if [ $? -eq 32 ]; then 217 tst_res TCONF "$cmd returned TCONF" 218 return 219 fi 220 fi 221 tst_res TINFO "using command: $cmd" 222 223 boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2) 224 if [ -z "$boot_aggregate" ]; then 225 tst_res TFAIL "failed to get boot aggregate" 226 return 227 fi 228 tst_res TINFO "IMA boot aggregate: '$boot_aggregate'" 229 230 if [ "$DIGEST" = "$boot_aggregate" ]; then 231 tst_res TPASS "bios boot aggregate matches IMA boot aggregate" 232 else 233 tst_res TFAIL "bios boot aggregate does not match IMA boot aggregate ($DIGEST)" 234 fi 235} 236 237test1() 238{ 239 tst_res TINFO "verify boot aggregate" 240 241 # deliberately try test1_hw_tpm() if test1_tpm_bypass_mode() fails 242 [ -z "$TPM_VERSION" ] && test1_tpm_bypass_mode || test1_hw_tpm 243} 244 245test2() 246{ 247 local hash pcr_aggregate out ret 248 249 tst_res TINFO "verify PCR values" 250 251 if [ "$MAYBE_TPM2" = 1 ]; then 252 tst_res TINFO "TPM version not detected ($ERRMSG_TPM), assume TPM 2" 253 TPM_VERSION=2 254 fi 255 256 if [ -z "$TPM_VERSION" ]; then 257 tst_brk TCONF "TPM version not detected ($ERRMSG_TPM)" 258 fi 259 260 if [ "$ALGORITHM" = "sha1" -a "$MISSING_EVMCTL" = 1 ]; then 261 tst_check_cmds evmctl || return 1 262 fi 263 264 out=$(read_pcr_tpm$TPM_VERSION) 265 ret=$? 266 267 if [ $ret -ne 0 ]; then 268 case "$ret" in 269 1) tst_res TFAIL "$out";; 270 32) tst_res TCONF "$out";; 271 *) tst_brk TBROK "unsupported return type '$1'";; 272 esac 273 return 274 fi 275 276 hash=$(echo "$out" | grep "^PCR-10" | cut -d' ' -f2) 277 278 if [ -z "$out" ]; then 279 tst_res TFAIL "PCR-10 hash not found" 280 return 281 fi 282 283 tst_res TINFO "real PCR-10: '$hash'" 284 285 get_pcr10_aggregate > tmp.txt 286 pcr_aggregate="$(cat tmp.txt)" 287 if [ -z "$pcr_aggregate" ]; then 288 return 289 fi 290 tst_res TINFO "aggregate PCR-10: '$pcr_aggregate'" 291 292 if [ "$hash" = "$pcr_aggregate" ]; then 293 tst_res TPASS "aggregate PCR value matches real PCR value" 294 else 295 tst_res TFAIL "aggregate PCR value does not match real PCR value" 296 fi 297} 298 299tst_run 300