123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- #!/bin/sh -ue
- # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- #
- # Usage: dev_debug_vboot [ --cleanup | DIRECTORY ]
- #
- # This extracts some useful debugging information about verified boot. A short
- # summary is printed on stdout, more detailed information and working files are
- # left in a log directory.
- #
- ##############################################################################
- # Clean up PATH for root use. Note that we're assuming [ is always built-in.
- [ "${EUID:-0}" = 0 ] && PATH=/bin:/sbin:/usr/bin:/usr/sbin
- PUBLOGFILE="/var/log/debug_vboot_noisy.log"
- OPT_CLEANUP=
- OPT_BIOS=
- OPT_FORCE=
- OPT_IMAGE=
- OPT_KERNEL=
- OPT_VERBOSE=
- FLAG_SAVE_LOG_FILE=yes
- LOGFILE=/dev/stdout
- TMPDIR=
- ##############################################################################
- usage() {
- local prog
- prog=${0##*/}
- cat <<EOF
- Usage: $prog [options] [DIRECTORY]
- This logs as much as it can about the verified boot process. With no arguments
- it will attempt to read the current BIOS, extract the firmware keys, and use
- those keys to validate all the ChromeOS kernel partitions it can find. A
- summary output is printed on stdout, and the detailed log is copied to
- $PUBLOGFILE afterwards.
- If a directory is given, it will attempt to use the components from that
- directory and will leave the detailed log in that directory.
- Options:
- -b FILE, --bios FILE Specify the BIOS image to use
- -i FILE, --image FILE Specify the disk image to use
- -k FILE, --kernel FILE Specify the kernel partition image to use
- -v Spew the detailed log to stdout
- -c, --cleanup Delete the DIRECTORY when done
- -h, --help Print this help message and exit
- EOF
- exit 0
- }
- cleanup() {
- if [ -n "${FLAG_SAVE_LOG_FILE}" ]; then
- if cp -f "${LOGFILE}" "${PUBLOGFILE}" 2>/dev/null; then
- info "Exporting log file as ${PUBLOGFILE}"
- fi
- fi
- if [ -n "${OPT_CLEANUP}" ] && [ -d "${TMPDIR}" ] ; then
- cd /
- rm -rf "${TMPDIR}"
- fi
- }
- die() {
- echo "$*" 1>&2
- exit 1
- }
- info() {
- echo "$@"
- echo "#" "$@" >> "$LOGFILE"
- }
- infon() {
- echo -n "$@"
- echo "#" "$@" >> "$LOGFILE"
- }
- debug() {
- echo "#" "$@" >> "$LOGFILE"
- }
- log() {
- echo "+" "$@" >> "$LOGFILE"
- "$@" >> "$LOGFILE" 2>&1
- }
- loghead() {
- echo "+" "$@" "| head" >> "$LOGFILE"
- "$@" | head >> "$LOGFILE" 2>&1
- }
- logdie() {
- echo "+ERROR:" "$@" >> "$LOGFILE"
- die "$@"
- }
- result() {
- LAST_RESULT=$?
- if [ "${LAST_RESULT}" = "0" ]; then
- info "OK"
- else
- info "FAILED"
- fi
- }
- require_utils() {
- local missing
- missing=
- for tool in $* ; do
- if ! type "$tool" >/dev/null 2>&1 ; then
- missing="$missing $tool"
- fi
- done
- if [ -n "$missing" ]; then
- logdie "can't find these programs: $missing"
- fi
- }
- extract_kerns_from_file() {
- local start
- local size
- local part
- local rest
- debug "Extracting kernel partitions from $1 ..."
- cgpt find -v -t kernel "$1" | grep 'Label:' |
- while read start size part rest; do
- name="part_${part}"
- log dd if="$1" bs=512 skip=${start} count=${size} of="${name}" &&
- echo "${name}"
- done
- }
- format_as_tpm_version() {
- local a
- local b
- local what
- local num
- local rest
- a='/(Data|Kernel) key version/ {print $1,$4}'
- b='/Kernel version/ {print $1, $3}'
- awk "$a $b" "$1" | while read what num rest; do
- [ "${what}" = "Data" ] && block="${num}"
- [ "${what}" = "Kernel" ] && printf '0x%04x%04x' "${block}" "${num}"
- done
- }
- fix_old_names() {
- # Convert any old-style names to new-style
- [ -f GBB_Area ] && log mv -f GBB_Area GBB
- [ -f Firmware_A_Key ] && log mv -f Firmware_A_Key VBLOCK_A
- [ -f Firmware_B_Key ] && log mv -f Firmware_B_Key VBLOCK_B
- [ -f Firmware_A_Data ] && log mv -f Firmware_A_Data FW_MAIN_A
- [ -f Firmware_B_Data ] && log mv -f Firmware_B_Data FW_MAIN_B
- true
- }
- ##############################################################################
- # Here we go...
- umask 022
- # defaults
- DEV_DEBUG_FORCE=
- # override them?
- [ -f /etc/default/vboot_reference ] && . /etc/default/vboot_reference
- # Pre-parse args to replace actual args with a sanitized version.
- TEMP=$(getopt -o hvb:i:k:cf --long help,bios:,image:,kernel:,cleanup,force \
- -n $0 -- "$@")
- eval set -- "$TEMP"
- # Now look at them.
- while true ; do
- case "${1:-}" in
- -b|--bios)
- OPT_BIOS=$(readlink -f "$2")
- shift 2
- FLAG_SAVE_LOG_FILE=
- ;;
- -i|--image=*)
- OPT_IMAGE=$(readlink -f "$2")
- shift 2
- FLAG_SAVE_LOG_FILE=
- ;;
- -k|--kernel)
- OPT_KERNEL=$(readlink -f "$2")
- shift 2
- FLAG_SAVE_LOG_FILE=
- ;;
- -c|--cleanup)
- OPT_CLEANUP=yes
- shift
- ;;
- -f|--force)
- OPT_FORCE=yes
- shift
- ;;
- -v)
- OPT_VERBOSE=yes
- shift
- FLAG_SAVE_LOG_FILE=
- ;;
- -h|--help)
- usage
- break
- ;;
- --)
- shift
- break
- ;;
- *)
- die "Internal error in option parsing"
- ;;
- esac
- done
- if [ -z "${1:-}" ]; then
- TMPDIR=$(mktemp -d /tmp/debug_vboot_XXXXXXXXX)
- else
- TMPDIR="$1"
- [ -d ${TMPDIR} ] || die "$TMPDIR doesn't exist"
- FLAG_SAVE_LOG_FILE=
- fi
- [ -z "${OPT_VERBOSE}" ] && LOGFILE="${TMPDIR}/noisy.log"
- [ -d ${TMPDIR} ] || mkdir -p ${TMPDIR} || exit 1
- cd ${TMPDIR} || exit 1
- echo "Running $0 $*" > "$LOGFILE"
- log date
- debug "DEV_DEBUG_FORCE=($DEV_DEBUG_FORCE)"
- debug "OPT_CLEANUP=($OPT_CLEANUP)"
- debug "OPT_BIOS=($OPT_BIOS)"
- debug "OPT_FORCE=($OPT_FORCE)"
- debug "OPT_IMAGE=($OPT_IMAGE)"
- debug "OPT_KERNEL=($OPT_KERNEL)"
- debug "FLAG_SAVE_LOG_FILE=($FLAG_SAVE_LOG_FILE)"
- echo "Saving verbose log as $LOGFILE"
- trap cleanup EXIT
- if [ -n "${DEV_DEBUG_FORCE}" ] && [ -z "${OPT_FORCE}" ]; then
- info "Not gonna do anything without the --force option."
- exit 0
- fi
- # Make sure we have the programs we need
- need="futility"
- [ -z "${OPT_BIOS}" ] && need="$need flashrom"
- [ -z "${OPT_KERNEL}" ] && need="$need cgpt"
- require_utils $need
- # Assuming we're on a ChromeOS device, see what we know.
- set +e
- log crossystem --all
- log rootdev -s
- log ls -aCF /root
- log ls -aCF /mnt/stateful_partition
- devs=$(awk '/(mmcblk[0-9])$|(sd[a-z])$|(nvme[0-9]+n[0-9]+)$/ {print "/dev/"$4}' /proc/partitions)
- for d in $devs; do
- log cgpt show $d
- done
- log flashrom -V -p host --wp-status
- tpm_fwver=$(crossystem tpm_fwver) || tpm_fwver="UNKNOWN"
- tpm_kernver=$(crossystem tpm_kernver) || tpm_kernver="UNKNOWN"
- set -e
- info "Extracting BIOS components..."
- if [ -n "${OPT_BIOS}" ]; then
- # If we've already got a file, just extract everything.
- log futility dump_fmap -x "${OPT_BIOS}"
- fix_old_names
- else
- # First try pulling just the components we want (using new-style names)
- if log flashrom -p host -r /dev/null \
- -i"GBB":GBB \
- -i"FMAP":FMAP \
- -i"VBLOCK_A":VBLOCK_A \
- -i"VBLOCK_B":VBLOCK_B \
- -i"FW_MAIN_A":FW_MAIN_A \
- -i"FW_MAIN_B":FW_MAIN_B ; then
- log futility dump_fmap FMAP
- else
- info "Couldn't read individual components. Read the whole thing..."
- if log flashrom -p host -r bios.rom ; then
- log futility dump_fmap -x bios.rom
- fix_old_names
- else
- logdie "Can't read BIOS at all. Giving up."
- fi
- fi
- fi
- info "Pulling root and recovery keys from GBB..."
- log futility gbb_utility -g --rootkey rootkey.vbpubk \
- --recoverykey recoverykey.vbpubk \
- "GBB" || logdie "Unable to extract keys from GBB"
- log futility vbutil_key --unpack rootkey.vbpubk
- log futility vbutil_key --unpack recoverykey.vbpubk
- futility vbutil_key --unpack rootkey.vbpubk |
- grep -q b11d74edd286c144e1135b49e7f0bc20cf041f10 &&
- info " Looks like dev-keys"
- # Okay if one of the firmware verifications fails
- set +e
- for fw in A B; do
- infon "Verify firmware ${fw} with root key: "
- log futility vbutil_firmware --verify "VBLOCK_${fw}" \
- --signpubkey rootkey.vbpubk \
- --fv "FW_MAIN_${fw}" --kernelkey "kern_subkey_${fw}.vbpubk" ; result
- if [ "${LAST_RESULT}" = "0" ]; then
- # rerun to get version numbers
- futility vbutil_firmware --verify "VBLOCK_${fw}" \
- --signpubkey rootkey.vbpubk \
- --fv "FW_MAIN_${fw}" > tmp.txt
- ver=$(format_as_tpm_version tmp.txt)
- info " TPM=${tpm_fwver}, this=${ver}"
- fi
- done
- set -e
- info "Examining kernels..."
- if [ -n "${OPT_KERNEL}" ]; then
- kernparts="${OPT_KERNEL}"
- elif [ -n "${OPT_IMAGE}" ]; then
- if [ -f "${OPT_IMAGE}" ]; then
- kernparts=$(extract_kerns_from_file "${OPT_IMAGE}")
- else
- kernparts=$(cgpt find -t kernel "${OPT_IMAGE}")
- fi
- else
- kernparts=$(cgpt find -t kernel)
- fi
- [ -n "${kernparts}" ] || logdie "No kernels found"
- # Okay if any of the kernel verifications fails
- set +e
- kc=0
- for kname in ${kernparts}; do
- if [ -f "${kname}" ]; then
- kfile="${kname}"
- else
- kfile="kern_${kc}"
- debug "copying ${kname} to ${kfile}..."
- log dd if="${kname}" of="${kfile}"
- fi
- infon "Kernel ${kname}: "
- log futility vbutil_keyblock --unpack "${kfile}" ; result
- if [ "${LAST_RESULT}" != "0" ]; then
- loghead od -Ax -tx1 "${kfile}"
- else
- # Test each kernel with each key
- for key in kern_subkey_A.vbpubk kern_subkey_B.vbpubk recoverykey.vbpubk; do
- infon " Verify ${kname} with $key: "
- log futility vbutil_kernel --verify "${kfile}" --signpubkey "$key" ; result
- if [ "${LAST_RESULT}" = "0" ]; then
- # rerun to get version numbers
- futility vbutil_kernel --verify "${kfile}" --signpubkey "$key" > tmp.txt
- ver=$(format_as_tpm_version tmp.txt)
- info " TPM=${tpm_kernver} this=${ver}"
- fi
- done
- fi
- kc=$(expr $kc + 1)
- done
- exit 0
|