signer: update legacy bootloader templates after image signing

Specifically, this patch updates 'root_hexdigest' in legacy bootloader
templates in EFI system partition to match the signed rootfs.

BRANCH=None
BUG=chromium:512940
TEST=Ran sign_official_build.sh locally and booted the image on kvm
(using BIOS).
TEST=Ran signing_unittests.py by locally changing vboot_stable_hash to
include this patch.

$ ./sign_official_build.sh base chromiumos_base_image.bin \
  ../../tests/devkeys chromiumos_base_image_signed.bin

Change-Id: Ied021c4464b113a64508f5081605069bdcecbc1f
Reviewed-on: https://chromium-review.googlesource.com/301742
Commit-Ready: Amey Deshpande <ameyd@google.com>
Tested-by: Amey Deshpande <ameyd@google.com>
Reviewed-by: Mike Frysinger <vapier@chromium.org>
diff --git a/scripts/image_signing/common_minimal.sh b/scripts/image_signing/common_minimal.sh
index be3d2ad..ad84c50 100644
--- a/scripts/image_signing/common_minimal.sh
+++ b/scripts/image_signing/common_minimal.sh
@@ -162,20 +162,18 @@
 # If the kernel is buggy and is unable to loop+mount quickly,
 # retry the operation a few times.
 # Args: IMAGE PARTNUM MOUNTDIRECTORY [ro]
+#
+# This function does not check whether the partition is allowed to be mounted as
+# RW.  Callers must ensure the partition can be mounted as RW before calling
+# this function without |ro| argument.
 _mount_image_partition_retry() {
   local image=$1
   local partnum=$2
   local mount_dir=$3
   local ro=$4
-  local offset=$(( $(partoffset "$image" "$partnum") * 512 ))
+  local offset=$(( $(partoffset "${image}" "${partnum}") * 512 ))
   local out try
 
-  if [ "$ro" != "ro" ]; then
-    # Forcibly call enable_rw_mount.  It should fail on unsupported
-    # filesystems and be idempotent on ext*.
-    enable_rw_mount "$image" ${offset} 2> /dev/null
-  fi
-
   set -- sudo LC_ALL=C mount -o loop,offset=${offset},${ro} \
     "${image}" "${mount_dir}"
   try=1
@@ -204,19 +202,38 @@
   return 1
 }
 
+# If called without 'ro', make sure the partition is allowed to be mounted as
+# 'rw' before actually mounting it.
+# Args: IMAGE PARTNUM MOUNTDIRECTORY [ro]
+_mount_image_partition() {
+  local image=$1
+  local partnum=$2
+  local mount_dir=$3
+  local ro=$4
+  local offset=$(( $(partoffset "${image}" "${partnum}") * 512 ))
+
+  if [ "$ro" != "ro" ]; then
+    # Forcibly call enable_rw_mount.  It should fail on unsupported
+    # filesystems and be idempotent on ext*.
+    enable_rw_mount "${image}" ${offset} 2> /dev/null
+  fi
+
+  _mount_image_partition_retry "$@"
+}
+
 # Mount a partition read-only from an image into a local directory
 # Args: IMAGE PARTNUM MOUNTDIRECTORY
 mount_image_partition_ro() {
-  _mount_image_partition_retry "$@" "ro"
+  _mount_image_partition "$@" "ro"
 }
 
 # Mount a partition from an image into a local directory
 # Args: IMAGE PARTNUM MOUNTDIRECTORY
 mount_image_partition() {
   local mount_dir=$3
-  _mount_image_partition_retry "$@"
-  if is_rootfs_partition "$mount_dir"; then
-    tag_as_needs_to_be_resigned "$mount_dir"
+  _mount_image_partition "$@"
+  if is_rootfs_partition "${mount_dir}"; then
+    tag_as_needs_to_be_resigned "${mount_dir}"
   fi
 }
 
diff --git a/scripts/image_signing/sign_official_build.sh b/scripts/image_signing/sign_official_build.sh
index 72d4f58..d1c8768 100755
--- a/scripts/image_signing/sign_official_build.sh
+++ b/scripts/image_signing/sign_official_build.sh
@@ -695,6 +695,55 @@
   replace_image_partition ${image_bin} 2 ${updated_kimagea}
 }
 
+# Update the legacy bootloader templates in EFI partition if available.
+# Args: IMAGE_BIN DM_PARTNO
+update_legacy_bootloader() {
+  local image="$1"
+  local dm_partno="$2"
+
+  local esp_partnum=12
+  local esp_offset=$(( $(partoffset "${image}" "${esp_partnum}") * 512 ))
+  # Check if the image has an ESP partition.
+  if [[ "${esp_offset}" == "0" ]]; then
+    info "Not updating legacy bootloader configs: ${image}"
+    return 0
+  fi
+
+  local esp_dir="$(make_temp_dir)"
+  # We use the 'unsafe' variant because the EFI system partition is vfat type
+  # and can be mounted in RW mode.
+  _mount_image_partition_retry "${image}" "${esp_partnum}" "${esp_dir}"
+
+  # If we can't find the dm parameter in the kernel config, bail out now.
+  local kernel_config=$(grab_kernel_config "${image}" "${dm_partno}")
+  local root_hexdigest="$(get_hash_from_config "${kernel_config}")"
+  if [[ -z "${root_hexdigest}" ]]; then
+    error "Couldn't grab root_digest from kernel partition ${dm_partno}"
+    error " (config: ${kernel_config})"
+    return 1
+  fi
+  # Update syslinux configs for legacy BIOS systems.
+  if [[ -d "${esp_dir}/syslinux" ]]; then
+    local cfg=("${esp_dir}"/syslinux/*.cfg)
+    if ! sudo sed -i -r \
+      "s/\broot_hexdigest=[a-z0-9]+/root_hexdigest=${root_hexdigest}/g" \
+      "${cfg[@]}"; then
+        error "Updating syslinux configs failed: '${cfg[*]}'"
+        return 1
+    fi
+  fi
+  # Update grub configs for EFI systems.
+  local grub_cfg="${esp_dir}/efi/boot/grub.cfg"
+  if [[ -f "${grub_cfg}" ]]; then
+    if ! sudo sed -i -r \
+      "s/\broot_hexdigest=[a-z0-9]+/root_hexdigest=${root_hexdigest}/g" \
+      "${grub_cfg}"; then
+        error "Updating grub config failed: '${grub_cfg}'"
+        return 1
+    fi
+  fi
+}
+
 # Sign an image file with proper keys.
 # Args: IMAGE_TYPE INPUT OUTPUT DM_PARTNO KERN_A_KEYBLOCK KERN_A_PRIVKEY \
 #       KERN_B_KEYBLOCK KERN_B_PRIVKEY
@@ -735,6 +784,10 @@
   if [[ "${image_type}" == "recovery" ]]; then
     update_recovery_kernel_hash "${output}"
   fi
+  if ! update_legacy_bootloader "${output}" "${dm_partno}"; then
+    # Error is already logged.
+    return 1
+  fi
   echo "Signed ${image_type} image output to ${output}"
 }