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