388 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			388 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
#!/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])$/ {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
 |