diff options
author | Hung-Te Lin <hungte@chromium.org> | 2010-10-16 09:37:32 +0800 |
---|---|---|
committer | Hung-Te Lin <hungte@chromium.org> | 2010-10-16 09:37:32 +0800 |
commit | 20525b91644a786e966c9486ac9afdf3d7c5447f (patch) | |
tree | a598434c6860623790ed55fa470258fe2418ffd3 | |
parent | c5175e3ca3c6f2318aafd5c1e59a011b5369ee1c (diff) | |
download | vboot-20525b91644a786e966c9486ac9afdf3d7c5447f.tar.gz |
make_dev_ssd: new script to change SSD image to dev key
The make_dev_ssd.sh is made for devinstall shim to
change SSD kernels to be signed by dev keys.
- Kernel A, B will be resigned with dev keys (ignore if A/B seems not bootable)
- Adding param --remove_rootfs_verification can even disable rootfs hash check
This CL also includes some shared refine/fix to make_dev_firmware.sh
BUG=chrome-os-partner:1276
TEST=sudo ./make_dev_ssd.sh; (seeing Kernel A is resigned and B is ignored)
then reboot without developer mode (OK),
rootdev shows /dev/dm-0, rootdev -s shows /dev/sda3
sudo ./make_dev_ssd.sh --remove_rootfs_verification;
then reboot without developer mode (OK), rootdev shows /dev/sda3
Change-Id: Ic20f734b2af42e50a43c19a565a166a39d57a7fd
Review URL: http://codereview.chromium.org/3772013
-rwxr-xr-x | scripts/image_signing/common.sh | 50 | ||||
-rwxr-xr-x | scripts/image_signing/make_dev_firmware.sh | 67 | ||||
-rwxr-xr-x | scripts/image_signing/make_dev_ssd.sh | 236 | ||||
-rw-r--r-- | tests/devkeys/firmware_bmpfv.bin | bin | 0 -> 253568 bytes |
4 files changed, 297 insertions, 56 deletions
diff --git a/scripts/image_signing/common.sh b/scripts/image_signing/common.sh index b05ec500..7b6dc9f3 100755 --- a/scripts/image_signing/common.sh +++ b/scripts/image_signing/common.sh @@ -12,12 +12,20 @@ GPT=cgpt # The tag when the rootfs is changed. TAG_NEEDS_TO_BE_SIGNED="/root/.need_to_be_signed" -# Load shflags -if [ -f /usr/lib/shflags ]; then - . /usr/lib/shflags -else - . "${SCRIPT_DIR}/lib/shflags/shflags" -fi +# Finds and loads the 'shflags' library, or return as failed. +load_shflags() { + # Load shflags + if [ -f /usr/lib/shflags ]; then + . /usr/lib/shflags + elif [ -f "${SCRIPT_DIR}/shflags" ]; then + . "${SCRIPT_DIR}/shflags" + elif [ -f "${SCRIPT_DIR}/lib/shflags/shflags" ]; then + . "${SCRIPT_DIR}/lib/shflags/shflags" + else + echo "ERROR: Cannot find the required shflags library." + return 1 + fi +} # List of Temporary files and mount points. TEMP_FILE_LIST=$(mktemp) @@ -46,27 +54,18 @@ tag_as_needs_to_be_resigned() { # Determines if the target file system has the tag for resign # Args: MOUNTDIRECTORY -# Returns: $FLAGS_TRUE if the tag is there, otherwise $FLAGS_FALSE +# Returns: true if the tag is there otherwise false has_needs_to_be_resigned_tag() { local mount_dir="$1" - if [ -f "$mount_dir/$TAG_NEEDS_TO_BE_SIGNED" ]; then - return ${FLAGS_TRUE} - else - return ${FLAGS_FALSE} - fi + [ -f "$mount_dir/$TAG_NEEDS_TO_BE_SIGNED" ] } # Determines if the target file system is a Chrome OS root fs # Args: MOUNTDIRECTORY -# Returns: $FLAGS_TRUE if MOUNTDIRECTORY looks like root fs, -# otherwise $FLAGS_FALSE +# Returns: true if MOUNTDIRECTORY looks like root fs, otherwise false is_rootfs_partition() { local mount_dir="$1" - if [ -f "$mount_dir/$(dirname "$TAG_NEEDS_TO_BE_SIGNED")" ]; then - return ${FLAGS_TRUE} - else - return ${FLAGS_FALSE} - fi + [ -f "$mount_dir/$(dirname "$TAG_NEEDS_TO_BE_SIGNED")" ] } # Mount a partition read-only from an image into a local directory @@ -149,5 +148,18 @@ cleanup_temps_and_mounts() { rm -rf $TEMP_DIR_LIST $TEMP_FILE_LIST } +# Returns true if all files in parameters exist. +ensure_files_exist() { + local filename return_value=0 + for filename in "$@"; do + if [ ! -f "$filename" -a ! -b "$filename" ]; then + echo "ERROR: Cannot find required file: $filename" + return_value=1 + fi + done + + return $return_value +} + trap "cleanup_temps_and_mounts" EXIT diff --git a/scripts/image_signing/make_dev_firmware.sh b/scripts/image_signing/make_dev_firmware.sh index aec2b8d3..b8bf6e92 100755 --- a/scripts/image_signing/make_dev_firmware.sh +++ b/scripts/image_signing/make_dev_firmware.sh @@ -5,19 +5,23 @@ # found in the LICENSE file. # # This script can change key (usually developer keys) in a firmware binary -# image or system live firmware, and assign proper HWID, BMPFV as well. +# image or system live firmware (EEPROM), and assign proper HWID, BMPFV as well. SCRIPT_BASE="$(dirname "$0")" . "$SCRIPT_BASE/common.sh" +load_shflags || exit 1 # Constants used by DEFINE_* +VBOOT_BASE='/usr/share/vboot' +DEFAULT_KEYS_FOLDER="$VBOOT_BASE/devkeys" +DEFAULT_BMPFV_FILE="$DEFAULT_KEYS_FOLDER/firmware_bmpfv.bin" DEFAULT_BACKUP_FOLDER='/mnt/stateful_partition/backups' # DEFINE_string name default_value description flag DEFINE_string from "" "Path of input file (empty for system live firmware)" "f" DEFINE_string to "" "Path of output file (empty for system live firmware)" "t" -DEFINE_string keys "$SCRIPT_BASE/keys" "Path of folder of dev keys" "k" -DEFINE_string bmpfv "$SCRIPT_BASE/rsrc/bmpfv.bin" "Path to the new bitmap FV" "" +DEFINE_string keys "$DEFAULT_KEYS_FOLDER" "Path to folder of dev keys" "k" +DEFINE_string bmpfv "$DEFAULT_BMPFV_FILE" "Path to the new bitmap FV" "" DEFINE_boolean force_backup \ $FLAGS_TRUE "Create backup even if source is not live" "" DEFINE_string backup_dir \ @@ -33,10 +37,10 @@ eval set -- "$FLAGS_ARGV" set -e # the image we are (temporary) working with -IMAGE=$(make_temp_file) +IMAGE="$(make_temp_file)" # a log file to keep the output results of executed command -EXEC_LOG=$(make_temp_file) +EXEC_LOG="$(make_temp_file)" # Functions # ---------------------------------------------------------------------------- @@ -77,6 +81,7 @@ read_image() { write_image() { if [ -z "$FLAGS_to" ]; then echo "Writing system live firmware..." + # TODO(hungte) we can enable partial write to make this faster if is_debug_mode; then flashrom -V -w "$IMAGE" else @@ -96,6 +101,8 @@ echo_dev_hwid() { # NOTE: Some DEV firmware image files may put GUID in HWID. # These are not officially supported and they will see "{GUID} DEV". + # Also there's some length limitation in chromeos_acpi/HWID, so + # a "{GUID} DEV" will become "{GUID} " in that case. if [ "$hwid" != "$hwid_no_dev" ]; then hwid="$hwid_no_dev" @@ -105,21 +112,6 @@ echo_dev_hwid() { echo "$hwid_dev" } -# Checks if the files given by parameters all exist. -check_exist_or_die() { - local file is_success=$FLAGS_TRUE - for file in "$@"; do - if [ ! -s "$file" ]; then - echo "ERROR: Cannot find required file: $file" - is_success=$FLAGS_FALSE - fi - done - - if [ "$is_success" = $FLAGS_FALSE ]; then - exit 1 - fi -} - # Main # ---------------------------------------------------------------------------- main() { @@ -133,19 +125,20 @@ main() { local is_from_live=0 local backup_image= - debug_msg "Pre-requisition check" - check_exist_or_die \ + debug_msg "Prerequisite check" + ensure_files_exist \ "$root_pubkey" \ "$recovery_pubkey" \ "$firmware_keyblock" \ "$firmware_prvkey" \ "$kernel_sub_pubkey" \ - "$new_bmpfv" + "$new_bmpfv" || + exit 1 if [ -z "$FLAGS_from" ]; then is_from_live=1 else - check_exist_or_die "$FLAGS_from" + ensure_files_exist "$FLAGS_from" fi # TODO(hungte) check if GPIO.3 (WP) is enabled @@ -156,27 +149,26 @@ main() { debug_msg "Prepare to backup the file" if [ -n "$is_from_live" -o $FLAGS_force_backup = $FLAGS_TRUE ]; then - backup_image=$(make_temp_file) + backup_image="$(make_temp_file)" debug_msg "Creating backup file to $backup_image..." cp -f "$IMAGE" "$backup_image" fi # TODO(hungte) We can use vbutil_firmware to check if the current firmware is - # valid, so that we can know both they key, vbutil_firmware are working fine. + # valid so that we know keys and vbutil_firmware are all working fine. echo "Preparing new firmware image..." - debug_msg "Extract current HWID and rootkey" local old_hwid - old_hwid=$(gbb_utility --get --hwid "$IMAGE" 2>"$EXEC_LOG" | - grep '^hardware_id:' | - sed 's/^hardware_id: //') + old_hwid="$(gbb_utility --get --hwid "$IMAGE" 2>"$EXEC_LOG" | + grep '^hardware_id:' | + sed 's/^hardware_id: //')" debug_msg "Decide new HWID" if [ -z "$old_hwid" ]; then err_die "Cannot find current HWID. (message: $(cat "$EXEC_LOG"))" fi - local new_hwid=$(echo_dev_hwid "$old_hwid") + local new_hwid="$(echo_dev_hwid "$old_hwid")" debug_msg "Replace GBB parts (gbb_utility allows changing on-the-fly)" gbb_utility --set \ @@ -188,7 +180,7 @@ main() { err_die "Failed to change GBB Data. (message: $(cat "$EXEC_LOG"))" debug_msg "Resign the firmware code (A/B) with new keys" - local unsigned_image=$(make_temp_file) + local unsigned_image="$(make_temp_file)" cp -f "$IMAGE" "$unsigned_image" "$SCRIPT_BASE/resign_firmwarefd.sh" \ "$unsigned_image" \ @@ -200,21 +192,22 @@ main() { # TODO(hungte) compare if the image really needs to be changed. - debug_msg "Backup files when reading from system live image." + debug_msg "Check if we need to make backup file(s)" if [ -n "$backup_image" ]; then - local backup_hwid_name=$(echo "$old_hwid" | sed 's/ /_/g') - local backup_date_time=$(date +'%Y%m%d_%H%M%S') + local backup_hwid_name="$(echo "$old_hwid" | sed 's/ /_/g')" + local backup_date_time="$(date +'%Y%m%d_%H%M%S')" local backup_file_name="firmware_${backup_hwid_name}_${backup_date_time}.fd" local backup_file_path="$FLAGS_backup_dir/$backup_file_name" if mkdir -p "$FLAGS_backup_dir" && cp -f "$backup_image" "$backup_file_path"; then echo "Backup of current firmware image is stored in: $backup_file_path" else - echo "Cannot create file in $FLAGS_backup_dir... Ignore backups." + echo "WARNING: Cannot create file in $FLAGS_backup_dir... Ignore backups." fi fi # TODO(hungte) use vbutil_firmware to check if the new firmware is valid. + # Or, do verification in resign_firmwarefd.sh and trust it. debug_msg "Write the image" write_image || @@ -224,7 +217,7 @@ main() { if [ -z "$FLAGS_to" ]; then echo "Successfully changed firmware to Developer Keys. New HWID: $new_hwid" else - echo "Firmware image '$FLAGS_to' now uses Developer Keys. HWID: $new_hwid" + echo "Firmware '$FLAGS_to' now uses Developer Keys. New HWID: $new_hwid" fi } diff --git a/scripts/image_signing/make_dev_ssd.sh b/scripts/image_signing/make_dev_ssd.sh new file mode 100755 index 00000000..a5a961d6 --- /dev/null +++ b/scripts/image_signing/make_dev_ssd.sh @@ -0,0 +1,236 @@ +#!/bin/sh +# +# Copyright (c) 2010 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. +# +# This script can change key (usually developer keys) and kernel config +# of a kernels on SSD. + +SCRIPT_BASE="$(dirname "$0")" +. "$SCRIPT_BASE/common.sh" +load_shflags || exit 1 + +# Constants used by DEFINE_* +VBOOT_BASE='/usr/share/vboot' +DEFAULT_KEYS_FOLDER="$VBOOT_BASE/devkeys" +DEFAULT_BACKUP_FOLDER='/mnt/stateful_partition/backups' + +# DEFINE_string name default_value description flag +DEFINE_string image "/dev/sda" "Path to device or image file" "i" +DEFINE_string keys "$DEFAULT_KEYS_FOLDER" "Path to folder of dev keys" "k" +DEFINE_boolean remove_rootfs_verification \ + $FLAGS_FALSE "Modify kernel boot config to disable rootfs verification" "" +DEFINE_string backup_dir \ + "$DEFAULT_BACKUP_FOLDER" "Path of directory to store kernel backups" "" +DEFINE_boolean debug $FLAGS_FALSE "Provide debug messages" "d" + +# Parse command line +FLAGS "$@" || exit 1 +eval set -- "$FLAGS_ARGV" + +# Globals +# ---------------------------------------------------------------------------- +set -e + +# a log file to keep the output results of executed command +EXEC_LOG="$(make_temp_file)" + +# Functions +# ---------------------------------------------------------------------------- +# Reports error message and exit(1) +err_die() { + echo "ERROR: $*" 1>&2 + exit 1 +} + +# Returns true if we're running in debug mode +is_debug_mode() { + [ "$FLAGS_debug" = $FLAGS_TRUE ] +} + +# Prints messages (in parameters) in debug mode +debug_msg() { + if is_debug_mode; then + echo "DEBUG: $*" 1>&2 + fi +} + +# Removes rootfs verification from kernel boot parameter +remove_rootfs_verification() { + echo "$*" | sed ' + s|dm_verity[^ ]\+||g + s| ro | rw | + s|verity /dev/sd%D%P /dev/sd%D%P || + s| root=/dev/dm-0 | root=/dev/sd%D%P | + s|dm="[^"]\+" ||' +} + +# Wrapped version of dd +mydd() { + # oflag=sync is safer, but since we need bs=512, syncing every block would be + # very slow. + dd "$@" >"$EXEC_LOG" 2>&1 || + err_die "Failed in [dd $@], Message: $(cat "$EXEC_LOG")" +} + +# Prints a more friendly name from kernel index number +cros_kernel_name() { + case $1 in + 2) + echo "Kernel A" + ;; + 4) + echo "Kernel B" + ;; + *) + err_die "unknown kernel index: $1" + esac +} + +# Resigns a kernel on SSD or image. +resign_ssd_kernel() { + # bs=512 is the fixed block size for dd and cgpt + local bs=512 + local ssd_device="$1" + + # reasonable size for current kernel partition + local min_kernel_size=32000 + local max_kernel_size=65536 + local resigned_kernels=0 + + for kernel_index in 2 4; do + local old_blob="$(make_temp_file)" + local new_blob="$(make_temp_file)" + local name="$(cros_kernel_name $kernel_index)" + + debug_msg "Probing $name information" + local offset size + offset="$(partoffset "$ssd_device" "$kernel_index")" || + err_die "Failed to get partition $kernel_index offset from $ssd_device" + size="$(partsize "$ssd_device" "$kernel_index")" || + err_die "Failed to get partition $kernel_index size from $ssd_device" + if [ ! $size -gt $min_kernel_size ]; then + echo "WARNING: $name seems too small ($size), ignored." + continue + fi + if [ ! $size -le $max_kernel_size ]; then + echo "WARNING: $name seems too large ($size), ignored." + continue + fi + + debug_msg "Reading $name from partition $kernel_index" + mydd if="$ssd_device" of="$old_blob" bs=$bs skip=$offset count=$size + + debug_msg "Checking if $name is valid" + local old_kernel_config + if ! old_kernel_config="$(dump_kernel_config "$old_blob" 2>"$EXEC_LOG")" + then + debug_msg "dump_kernel_config error message: $(cat "$EXEC_LOG")" + echo "WARNING: $name: no kernel boot information, ignored." + continue + fi + + debug_msg "Decide and prepare signing parameters" + local resign_command + # TODO(hungte) $KERNEL_KEYBLOCK and $new_kernel_config should also be + # quoted, but quoting inside would cause extra quote... We should find some + # better way to do this, for example using eval. + if [ ${FLAGS_remove_rootfs_verification} = $FLAGS_TRUE ]; then + local new_kernel_config_file="$(make_temp_file)" + remove_rootfs_verification "$old_kernel_config" >"$new_kernel_config_file" + resign_command="--config $new_kernel_config_file" + debug_msg "New kernel config: $(cat $new_kernel_config_file)" + echo "$name: Disabled rootfs verification." + else + resign_command="--vblockonly --keyblock $KERNEL_KEYBLOCK" + fi + + debug_msg "Re-signing $name from $old_blob to $new_blob" + debug_msg "Using key: $KERNEL_DATAKEY, command: $resign_command" + vbutil_kernel \ + --repack "$new_blob" \ + $resign_command \ + --signprivate "$KERNEL_DATAKEY" \ + --oldblob "$old_blob" >"$EXEC_LOG" 2>&1 || + err_die "Failed to resign $name. Message: $(cat "$EXEC_LOG")" + + debug_msg "Creating new kernel image (vboot+code+config)" + local new_kern="$(make_temp_file)" + cp "$old_blob" "$new_kern" + mydd if="$new_blob" of="$new_kern" conv=notrunc + + if is_debug_mode; then + debug_msg "for debug purposes, check *.dbgbin" + cp "$old_blob" old_blob.dbgbin + cp "$new_blob" new_blob.dbgbin + cp "$new_kern" new_kern.dbgbin + fi + + debug_msg "Verifying new kernel and keys" + vbutil_kernel \ + --verify "$new_kern" \ + --signpubkey "$KERNEL_PUBKEY" --verbose >"$EXEC_LOG" 2>&1 || + err_die "Failed to verify new $name. Message: $(cat "$EXEC_LOG")" + + debug_msg "Backup old kernel blob" + local backup_date_time="$(date +'%Y%m%d_%H%M%S')" + local backup_name="$(echo "$name" | sed 's/ /_/g; s/^K/k/')" + local backup_file_name="${backup_name}_${backup_date_time}.bin" + local backup_file_path="$FLAGS_backup_dir/$backup_file_name" + if mkdir -p "$FLAGS_backup_dir" && + cp -f "$old_blob" "$backup_file_path"; then + echo "Backup of $name is stored in: $backup_file_path" + else + echo "WARNING: Cannot create file in $FLAGS_backup_dir... Ignore backups." + fi + + debug_msg "Writing $name to partition $kernel_index" + mydd \ + if="$new_kern" \ + of="$ssd_device" \ + seek=$offset \ + bs=$bs \ + count=$size \ + conv=notrunc + resigned_kernels=$(($resigned_kernels + 1)) + + # Sometimes doing "dump_kernel_config" or other I/O now (or after return to + # shell) will get the data before modification. Not a problem now, but for + # safety, let's try to sync more. + sync; sync; sync + + echo "$name: Re-signed with developer keys successfully." + done + return $resigned_kernels +} + +# Main +# ---------------------------------------------------------------------------- +main() { + local num_signed=0 + # Check parameters + KERNEL_KEYBLOCK="$FLAGS_keys/kernel.keyblock" + KERNEL_DATAKEY="$FLAGS_keys/kernel_data_key.vbprivk" + KERNEL_PUBKEY="$FLAGS_keys/kernel_subkey.vbpubk" + + debug_msg "Prerequisite check" + ensure_files_exist \ + "$KERNEL_KEYBLOCK" \ + "$KERNEL_DATAKEY" \ + "$KERNEL_PUBKEY" \ + "$FLAGS_image" || + exit 1 + + resign_ssd_kernel "$FLAGS_image" || num_signed=$? + + debug_msg "Complete." + if [ $num_signed -gt 0 -a $num_signed -le 2 ]; then + # signed 1 or two kernels + echo "Successfully re-signed $num_signed kernel(s) on device $FLAGS_image". + else + err_die "Failed re-signing kernels." + fi +} + +main diff --git a/tests/devkeys/firmware_bmpfv.bin b/tests/devkeys/firmware_bmpfv.bin Binary files differnew file mode 100644 index 00000000..3e9af976 --- /dev/null +++ b/tests/devkeys/firmware_bmpfv.bin |