dev_debug_vboot 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #!/bin/sh -ue
  2. # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. #
  6. # Usage: dev_debug_vboot [ --cleanup | DIRECTORY ]
  7. #
  8. # This extracts some useful debugging information about verified boot. A short
  9. # summary is printed on stdout, more detailed information and working files are
  10. # left in a log directory.
  11. #
  12. ##############################################################################
  13. # Clean up PATH for root use. Note that we're assuming [ is always built-in.
  14. [ "${EUID:-0}" = 0 ] && PATH=/bin:/sbin:/usr/bin:/usr/sbin
  15. PUBLOGFILE="/var/log/debug_vboot_noisy.log"
  16. OPT_CLEANUP=
  17. OPT_BIOS=
  18. OPT_FORCE=
  19. OPT_IMAGE=
  20. OPT_KERNEL=
  21. OPT_VERBOSE=
  22. FLAG_SAVE_LOG_FILE=yes
  23. LOGFILE=/dev/stdout
  24. TMPDIR=
  25. ##############################################################################
  26. usage() {
  27. local prog
  28. prog=${0##*/}
  29. cat <<EOF
  30. Usage: $prog [options] [DIRECTORY]
  31. This logs as much as it can about the verified boot process. With no arguments
  32. it will attempt to read the current BIOS, extract the firmware keys, and use
  33. those keys to validate all the ChromeOS kernel partitions it can find. A
  34. summary output is printed on stdout, and the detailed log is copied to
  35. $PUBLOGFILE afterwards.
  36. If a directory is given, it will attempt to use the components from that
  37. directory and will leave the detailed log in that directory.
  38. Options:
  39. -b FILE, --bios FILE Specify the BIOS image to use
  40. -i FILE, --image FILE Specify the disk image to use
  41. -k FILE, --kernel FILE Specify the kernel partition image to use
  42. -v Spew the detailed log to stdout
  43. -c, --cleanup Delete the DIRECTORY when done
  44. -h, --help Print this help message and exit
  45. EOF
  46. exit 0
  47. }
  48. cleanup() {
  49. if [ -n "${FLAG_SAVE_LOG_FILE}" ]; then
  50. if cp -f "${LOGFILE}" "${PUBLOGFILE}" 2>/dev/null; then
  51. info "Exporting log file as ${PUBLOGFILE}"
  52. fi
  53. fi
  54. if [ -n "${OPT_CLEANUP}" ] && [ -d "${TMPDIR}" ] ; then
  55. cd /
  56. rm -rf "${TMPDIR}"
  57. fi
  58. }
  59. die() {
  60. echo "$*" 1>&2
  61. exit 1
  62. }
  63. info() {
  64. echo "$@"
  65. echo "#" "$@" >> "$LOGFILE"
  66. }
  67. infon() {
  68. echo -n "$@"
  69. echo "#" "$@" >> "$LOGFILE"
  70. }
  71. debug() {
  72. echo "#" "$@" >> "$LOGFILE"
  73. }
  74. log() {
  75. echo "+" "$@" >> "$LOGFILE"
  76. "$@" >> "$LOGFILE" 2>&1
  77. }
  78. loghead() {
  79. echo "+" "$@" "| head" >> "$LOGFILE"
  80. "$@" | head >> "$LOGFILE" 2>&1
  81. }
  82. logdie() {
  83. echo "+ERROR:" "$@" >> "$LOGFILE"
  84. die "$@"
  85. }
  86. result() {
  87. LAST_RESULT=$?
  88. if [ "${LAST_RESULT}" = "0" ]; then
  89. info "OK"
  90. else
  91. info "FAILED"
  92. fi
  93. }
  94. require_utils() {
  95. local missing
  96. missing=
  97. for tool in $* ; do
  98. if ! type "$tool" >/dev/null 2>&1 ; then
  99. missing="$missing $tool"
  100. fi
  101. done
  102. if [ -n "$missing" ]; then
  103. logdie "can't find these programs: $missing"
  104. fi
  105. }
  106. extract_kerns_from_file() {
  107. local start
  108. local size
  109. local part
  110. local rest
  111. debug "Extracting kernel partitions from $1 ..."
  112. cgpt find -v -t kernel "$1" | grep 'Label:' |
  113. while read start size part rest; do
  114. name="part_${part}"
  115. log dd if="$1" bs=512 skip=${start} count=${size} of="${name}" &&
  116. echo "${name}"
  117. done
  118. }
  119. format_as_tpm_version() {
  120. local a
  121. local b
  122. local what
  123. local num
  124. local rest
  125. a='/(Data|Kernel) key version/ {print $1,$4}'
  126. b='/Kernel version/ {print $1, $3}'
  127. awk "$a $b" "$1" | while read what num rest; do
  128. [ "${what}" = "Data" ] && block="${num}"
  129. [ "${what}" = "Kernel" ] && printf '0x%04x%04x' "${block}" "${num}"
  130. done
  131. }
  132. fix_old_names() {
  133. # Convert any old-style names to new-style
  134. [ -f GBB_Area ] && log mv -f GBB_Area GBB
  135. [ -f Firmware_A_Key ] && log mv -f Firmware_A_Key VBLOCK_A
  136. [ -f Firmware_B_Key ] && log mv -f Firmware_B_Key VBLOCK_B
  137. [ -f Firmware_A_Data ] && log mv -f Firmware_A_Data FW_MAIN_A
  138. [ -f Firmware_B_Data ] && log mv -f Firmware_B_Data FW_MAIN_B
  139. true
  140. }
  141. ##############################################################################
  142. # Here we go...
  143. umask 022
  144. # defaults
  145. DEV_DEBUG_FORCE=
  146. # override them?
  147. [ -f /etc/default/vboot_reference ] && . /etc/default/vboot_reference
  148. # Pre-parse args to replace actual args with a sanitized version.
  149. TEMP=$(getopt -o hvb:i:k:cf --long help,bios:,image:,kernel:,cleanup,force \
  150. -n $0 -- "$@")
  151. eval set -- "$TEMP"
  152. # Now look at them.
  153. while true ; do
  154. case "${1:-}" in
  155. -b|--bios)
  156. OPT_BIOS=$(readlink -f "$2")
  157. shift 2
  158. FLAG_SAVE_LOG_FILE=
  159. ;;
  160. -i|--image=*)
  161. OPT_IMAGE=$(readlink -f "$2")
  162. shift 2
  163. FLAG_SAVE_LOG_FILE=
  164. ;;
  165. -k|--kernel)
  166. OPT_KERNEL=$(readlink -f "$2")
  167. shift 2
  168. FLAG_SAVE_LOG_FILE=
  169. ;;
  170. -c|--cleanup)
  171. OPT_CLEANUP=yes
  172. shift
  173. ;;
  174. -f|--force)
  175. OPT_FORCE=yes
  176. shift
  177. ;;
  178. -v)
  179. OPT_VERBOSE=yes
  180. shift
  181. FLAG_SAVE_LOG_FILE=
  182. ;;
  183. -h|--help)
  184. usage
  185. break
  186. ;;
  187. --)
  188. shift
  189. break
  190. ;;
  191. *)
  192. die "Internal error in option parsing"
  193. ;;
  194. esac
  195. done
  196. if [ -z "${1:-}" ]; then
  197. TMPDIR=$(mktemp -d /tmp/debug_vboot_XXXXXXXXX)
  198. else
  199. TMPDIR="$1"
  200. [ -d ${TMPDIR} ] || die "$TMPDIR doesn't exist"
  201. FLAG_SAVE_LOG_FILE=
  202. fi
  203. [ -z "${OPT_VERBOSE}" ] && LOGFILE="${TMPDIR}/noisy.log"
  204. [ -d ${TMPDIR} ] || mkdir -p ${TMPDIR} || exit 1
  205. cd ${TMPDIR} || exit 1
  206. echo "Running $0 $*" > "$LOGFILE"
  207. log date
  208. debug "DEV_DEBUG_FORCE=($DEV_DEBUG_FORCE)"
  209. debug "OPT_CLEANUP=($OPT_CLEANUP)"
  210. debug "OPT_BIOS=($OPT_BIOS)"
  211. debug "OPT_FORCE=($OPT_FORCE)"
  212. debug "OPT_IMAGE=($OPT_IMAGE)"
  213. debug "OPT_KERNEL=($OPT_KERNEL)"
  214. debug "FLAG_SAVE_LOG_FILE=($FLAG_SAVE_LOG_FILE)"
  215. echo "Saving verbose log as $LOGFILE"
  216. trap cleanup EXIT
  217. if [ -n "${DEV_DEBUG_FORCE}" ] && [ -z "${OPT_FORCE}" ]; then
  218. info "Not gonna do anything without the --force option."
  219. exit 0
  220. fi
  221. # Make sure we have the programs we need
  222. need="futility"
  223. [ -z "${OPT_BIOS}" ] && need="$need flashrom"
  224. [ -z "${OPT_KERNEL}" ] && need="$need cgpt"
  225. require_utils $need
  226. # Assuming we're on a ChromeOS device, see what we know.
  227. set +e
  228. log crossystem --all
  229. log rootdev -s
  230. log ls -aCF /root
  231. log ls -aCF /mnt/stateful_partition
  232. devs=$(awk '/(mmcblk[0-9])$|(sd[a-z])$|(nvme[0-9]+n[0-9]+)$/ {print "/dev/"$4}' /proc/partitions)
  233. for d in $devs; do
  234. log cgpt show $d
  235. done
  236. log flashrom -V -p host --wp-status
  237. tpm_fwver=$(crossystem tpm_fwver) || tpm_fwver="UNKNOWN"
  238. tpm_kernver=$(crossystem tpm_kernver) || tpm_kernver="UNKNOWN"
  239. set -e
  240. info "Extracting BIOS components..."
  241. if [ -n "${OPT_BIOS}" ]; then
  242. # If we've already got a file, just extract everything.
  243. log futility dump_fmap -x "${OPT_BIOS}"
  244. fix_old_names
  245. else
  246. # First try pulling just the components we want (using new-style names)
  247. if log flashrom -p host -r /dev/null \
  248. -i"GBB":GBB \
  249. -i"FMAP":FMAP \
  250. -i"VBLOCK_A":VBLOCK_A \
  251. -i"VBLOCK_B":VBLOCK_B \
  252. -i"FW_MAIN_A":FW_MAIN_A \
  253. -i"FW_MAIN_B":FW_MAIN_B ; then
  254. log futility dump_fmap FMAP
  255. else
  256. info "Couldn't read individual components. Read the whole thing..."
  257. if log flashrom -p host -r bios.rom ; then
  258. log futility dump_fmap -x bios.rom
  259. fix_old_names
  260. else
  261. logdie "Can't read BIOS at all. Giving up."
  262. fi
  263. fi
  264. fi
  265. info "Pulling root and recovery keys from GBB..."
  266. log futility gbb_utility -g --rootkey rootkey.vbpubk \
  267. --recoverykey recoverykey.vbpubk \
  268. "GBB" || logdie "Unable to extract keys from GBB"
  269. log futility vbutil_key --unpack rootkey.vbpubk
  270. log futility vbutil_key --unpack recoverykey.vbpubk
  271. futility vbutil_key --unpack rootkey.vbpubk |
  272. grep -q b11d74edd286c144e1135b49e7f0bc20cf041f10 &&
  273. info " Looks like dev-keys"
  274. # Okay if one of the firmware verifications fails
  275. set +e
  276. for fw in A B; do
  277. infon "Verify firmware ${fw} with root key: "
  278. log futility vbutil_firmware --verify "VBLOCK_${fw}" \
  279. --signpubkey rootkey.vbpubk \
  280. --fv "FW_MAIN_${fw}" --kernelkey "kern_subkey_${fw}.vbpubk" ; result
  281. if [ "${LAST_RESULT}" = "0" ]; then
  282. # rerun to get version numbers
  283. futility vbutil_firmware --verify "VBLOCK_${fw}" \
  284. --signpubkey rootkey.vbpubk \
  285. --fv "FW_MAIN_${fw}" > tmp.txt
  286. ver=$(format_as_tpm_version tmp.txt)
  287. info " TPM=${tpm_fwver}, this=${ver}"
  288. fi
  289. done
  290. set -e
  291. info "Examining kernels..."
  292. if [ -n "${OPT_KERNEL}" ]; then
  293. kernparts="${OPT_KERNEL}"
  294. elif [ -n "${OPT_IMAGE}" ]; then
  295. if [ -f "${OPT_IMAGE}" ]; then
  296. kernparts=$(extract_kerns_from_file "${OPT_IMAGE}")
  297. else
  298. kernparts=$(cgpt find -t kernel "${OPT_IMAGE}")
  299. fi
  300. else
  301. kernparts=$(cgpt find -t kernel)
  302. fi
  303. [ -n "${kernparts}" ] || logdie "No kernels found"
  304. # Okay if any of the kernel verifications fails
  305. set +e
  306. kc=0
  307. for kname in ${kernparts}; do
  308. if [ -f "${kname}" ]; then
  309. kfile="${kname}"
  310. else
  311. kfile="kern_${kc}"
  312. debug "copying ${kname} to ${kfile}..."
  313. log dd if="${kname}" of="${kfile}"
  314. fi
  315. infon "Kernel ${kname}: "
  316. log futility vbutil_keyblock --unpack "${kfile}" ; result
  317. if [ "${LAST_RESULT}" != "0" ]; then
  318. loghead od -Ax -tx1 "${kfile}"
  319. else
  320. # Test each kernel with each key
  321. for key in kern_subkey_A.vbpubk kern_subkey_B.vbpubk recoverykey.vbpubk; do
  322. infon " Verify ${kname} with $key: "
  323. log futility vbutil_kernel --verify "${kfile}" --signpubkey "$key" ; result
  324. if [ "${LAST_RESULT}" = "0" ]; then
  325. # rerun to get version numbers
  326. futility vbutil_kernel --verify "${kfile}" --signpubkey "$key" > tmp.txt
  327. ver=$(format_as_tpm_version tmp.txt)
  328. info " TPM=${tpm_kernver} this=${ver}"
  329. fi
  330. done
  331. fi
  332. kc=$(expr $kc + 1)
  333. done
  334. exit 0