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 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_brk TFAIL "evmctl failed $msg" 163 fi 164 165 [ $ret -ne 0 ] && tst_res TWARN "evmctl failed, trying to continue $msg" 166 167 pcr=$(grep -E "^($ALGORITHM: )*PCRAgg(.*10)*:" hash.txt | tail -1 \ 168 | awk '{print $NF}') 169 170 if [ -z "$pcr" ]; then 171 tst_res $res "failed to find aggregate PCR-10 $msg" 172 tst_res TINFO "hash file:" 173 cat hash.txt >&2 174 return 175 fi 176 177 echo "$pcr" 178} 179 180test1_tpm_bypass_mode() 181{ 182 local zero=$(echo $DIGEST | awk '{gsub(/./, "0")}; {print}') 183 184 if [ "$DIGEST" = "$zero" ]; then 185 tst_res TPASS "bios boot aggregate is $zero" 186 else 187 tst_res TFAIL "bios boot aggregate is not $zero ($DIGEST), kernel didn't export TPM 2.0 files for TPM device?" 188 return 1 189 fi 190} 191 192test1_hw_tpm() 193{ 194 local tpm_bios="$SECURITYFS/tpm0/binary_bios_measurements" 195 local cmd="evmctl ima_boot_aggregate -v" 196 local boot_aggregate 197 198 if [ -z "$TPM_VERSION" ]; then 199 tst_res TWARN "TPM-bypass failed, trying to test TPM device (unknown TPM version)" 200 MAYBE_TPM2=1 201 fi 202 203 if [ "$MISSING_EVMCTL" = 1 ]; then 204 if [ ! -f "$tpm_bios" ]; then 205 tst_res TCONF "missing $tpm_bios $ERRMSG_EVMCTL" 206 return 207 fi 208 tst_check_cmds ima_boot_aggregate || return 209 210 cmd="ima_boot_aggregate -f $tpm_bios" 211 212 # TCONF: libcrypto and openssl development packages required 213 $cmd 214 if [ $? -eq 32 ]; then 215 tst_res TCONF "$cmd returned TCONF" 216 return 217 fi 218 fi 219 tst_res TINFO "using command: $cmd" 220 221 boot_aggregate=$($cmd | grep "$ALGORITHM:" | cut -d':' -f2) 222 if [ -z "$boot_aggregate" ]; then 223 tst_res TFAIL "failed to get boot aggregate" 224 return 225 fi 226 tst_res TINFO "IMA boot aggregate: '$boot_aggregate'" 227 228 if [ "$DIGEST" = "$boot_aggregate" ]; then 229 tst_res TPASS "bios boot aggregate matches IMA boot aggregate" 230 else 231 tst_res TFAIL "bios boot aggregate does not match IMA boot aggregate ($DIGEST)" 232 fi 233} 234 235test1() 236{ 237 tst_res TINFO "verify boot aggregate" 238 239 # deliberately try test1_hw_tpm() if test1_tpm_bypass_mode() fails 240 [ -z "$TPM_VERSION" ] && test1_tpm_bypass_mode || test1_hw_tpm 241} 242 243test2() 244{ 245 local hash pcr_aggregate out ret 246 247 tst_res TINFO "verify PCR values" 248 249 if [ "$MAYBE_TPM2" = 1 ]; then 250 tst_res TINFO "TPM version not detected ($ERRMSG_TPM), assume TPM 2" 251 TPM_VERSION=2 252 fi 253 254 if [ -z "$TPM_VERSION" ]; then 255 tst_brk TCONF "TPM version not detected ($ERRMSG_TPM)" 256 fi 257 258 if [ "$ALGORITHM" = "sha1" -a "$MISSING_EVMCTL" = 1 ]; then 259 tst_check_cmds evmctl || return 1 260 fi 261 262 out=$(read_pcr_tpm$TPM_VERSION) 263 ret=$? 264 265 if [ $ret -ne 0 ]; then 266 case "$ret" in 267 1) tst_res TFAIL "$out";; 268 32) tst_res TCONF "$out";; 269 *) tst_brk TBROK "unsupported return type '$1'";; 270 esac 271 return 272 fi 273 274 hash=$(echo "$out" | grep "^PCR-10" | cut -d' ' -f2) 275 276 if [ -z "$out" ]; then 277 tst_res TFAIL "PCR-10 hash not found" 278 return 279 fi 280 281 tst_res TINFO "real PCR-10: '$hash'" 282 283 get_pcr10_aggregate > tmp.txt 284 pcr_aggregate="$(cat tmp.txt)" 285 if [ -z "$pcr_aggregate" ]; then 286 return 287 fi 288 tst_res TINFO "aggregate PCR-10: '$pcr_aggregate'" 289 290 if [ "$hash" = "$pcr_aggregate" ]; then 291 tst_res TPASS "aggregate PCR value matches real PCR value" 292 else 293 tst_res TFAIL "aggregate PCR value does not match real PCR value" 294 fi 295} 296 297. ima_setup.sh 298tst_run 299