diff options
author | Edward Hyunkoo Jee <edjee@google.com> | 2018-04-04 01:33:10 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-04-18 02:07:42 -0700 |
commit | 1493e938e45535f86b7132a83123c6319eacb217 (patch) | |
tree | 2855cc4f1e419608cf9c4019294de79bebee98a2 /scripts | |
parent | 2051af0c80c9a6cc82aa96fab7371a9475a107fe (diff) | |
download | vboot-1493e938e45535f86b7132a83123c6319eacb217.tar.gz |
image_signing: sign UEFI binaries
BUG=b:62189155
TEST=See CL:*601769
BRANCH=none
Change-Id: Id9569616bae0d5f44c1c96e18522ace244a5aae8
Reviewed-on: https://chromium-review.googlesource.com/995175
Commit-Ready: Edward Jee <edjee@google.com>
Tested-by: Edward Jee <edjee@google.com>
Reviewed-by: Jason Clinton <jclinton@chromium.org>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/image_signing/common_minimal.sh | 27 | ||||
-rwxr-xr-x | scripts/image_signing/install_gsetup_certs.sh | 70 | ||||
-rwxr-xr-x | scripts/image_signing/sign_official_build.sh | 71 | ||||
-rwxr-xr-x | scripts/image_signing/sign_uefi.sh | 107 | ||||
-rwxr-xr-x | scripts/image_signing/verify_uefi.sh | 79 |
5 files changed, 343 insertions, 11 deletions
diff --git a/scripts/image_signing/common_minimal.sh b/scripts/image_signing/common_minimal.sh index 43dfd109..d7ecc10e 100644 --- a/scripts/image_signing/common_minimal.sh +++ b/scripts/image_signing/common_minimal.sh @@ -252,6 +252,33 @@ mount_image_partition() { fi } +# Mount the image's ESP (EFI System Partition) on a newly created temporary +# directory. +# Prints out the newly created temporary directory path if succeeded, prints +# out nothing if ESP doesn't exist, print out "MOUNT_FAILED" if mount failed. +# Args: IMAGE ESP_PARTNUM +mount_image_esp() { + local image="$1" + local ESP_PARTNUM=12 + + local esp_offset=$(( $(partoffset "${image}" "${ESP_PARTNUM}") )) + # Check if the image has an ESP partition. + if [[ "${esp_offset}" == "0" ]]; then + return + 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. + if ! $(_mount_image_partition_retry "${image}" "${ESP_PARTNUM}" \ + "${esp_dir}" > /dev/null); then + echo "MOUNT_FAILED" + return + fi + + echo "${esp_dir}" +} + # Extract a partition to a file # Args: IMAGE PARTNUM OUTPUTFILE extract_image_partition() { diff --git a/scripts/image_signing/install_gsetup_certs.sh b/scripts/image_signing/install_gsetup_certs.sh new file mode 100755 index 00000000..d515b790 --- /dev/null +++ b/scripts/image_signing/install_gsetup_certs.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Copyright 2018 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. + +. "$(dirname "$0")/common.sh" + +set -e + +usage() { + cat <<EOF +Usage: $PROG /path/to/esp/dir /path/to/keys/dir + +Sign UEFI binaries in ESP. +EOF + if [[ $# -gt 0 ]]; then + error "$*" + exit 1 + fi + exit 0 +} + +install_gsetup_cert() { + local key_type="$1" + local cert="$2" + local gsetup_dir="$3" + if [[ -f "$cert" ]]; then + info "Putting ${key_type} cert: ${cert}" + local cert_basename="$(basename "${cert}")" + local der_filename="${cert_basename%.*}.der" + sudo mkdir -p "${gsetup_dir}/${key_type}" + sudo openssl x509 -in "${cert}" -inform PEM \ + -out "${gsetup_dir}/${key_type}/${der_filename}" -outform DER + else + info "No ${key_type} cert: ${cert}" + fi +} + +main() { + local esp_dir="$1" + local key_dir="$2" + + if [[ $# -ne 2 ]]; then + usage "command takes exactly 2 args" + fi + + local gsetup_dir="${esp_dir}/EFI/Google/GSetup" + + local pk_cert="${key_dir}/pk/pk.pem" + if [[ ! -f "${pk_cert}" ]]; then + die "No PK cert: ${pk_cert}" + fi + install_gsetup_cert pk "${pk_cert}" "${gsetup_dir}" + + local db_cert="${key_dir}/db/db.pem" + if [[ ! -f "${db_cert}" ]]; then + die "No DB cert: ${db_cert}" + fi + install_gsetup_cert db "${db_cert}" "${gsetup_dir}" + + local kek_cert="${key_dir}/kek/kek.pem" + install_gsetup_cert kek "${kek_cert}" "${gsetup_dir}" + + for dbx_cert in "${key_dir}/dbx/"*".pem"; do + install_gsetup_cert dbx "${dbx_cert}" "${gsetup_dir}" + done +} + +main "$@" diff --git a/scripts/image_signing/sign_official_build.sh b/scripts/image_signing/sign_official_build.sh index 7969179a..90fbed84 100755 --- a/scripts/image_signing/sign_official_build.sh +++ b/scripts/image_signing/sign_official_build.sh @@ -737,6 +737,60 @@ resign_android_image_if_exists() { info "Re-signed Android image" } +# Sign UEFI binaries, if possible. +sign_uefi_binaries() { + local image="$1" + + if [[ ! -d "${KEY_DIR}/uefi" ]]; then + return 0 + fi + + local esp_dir="$(mount_image_esp "${image}")" + if [[ -z "${esp_dir}" ]]; then + return 0 + elif [[ "${esp_dir}" == "MOUNT_FAILED" ]]; then + error "Could not mount EFI partition for signing UEFI binaries" + return 1 + fi + "${SCRIPT_DIR}/install_gsetup_certs.sh" "${esp_dir}" "${KEY_DIR}/uefi" + "${SCRIPT_DIR}/sign_uefi.sh" "${esp_dir}" "${KEY_DIR}/uefi" + sudo umount "${esp_dir}" + + local rootfs_dir="$(make_temp_dir)" + mount_image_partition "${image}" 3 "${rootfs_dir}" + "${SCRIPT_DIR}/sign_uefi.sh" "${rootfs_dir}/boot" "${KEY_DIR}/uefi" + sudo umount "${rootfs_dir}" + + info "Signed UEFI binaries" + return 0 +} + +verify_uefi_signatures() { + local image="$1" + local succeeded=1 + + local esp_dir="$(mount_image_esp "${image}")" + if [[ -z "${esp_dir}" ]]; then + return 0 + elif [[ "${esp_dir}" == "MOUNT_FAILED" ]]; then + error "Could not mount EFI partition for verifying UEFI signatures" + return 1 + fi + "${SCRIPT_DIR}/verify_uefi.sh" "${esp_dir}" "${esp_dir}" || succeeded=0 + + local rootfs_dir="$(make_temp_dir)" + mount_image_partition_ro "${image}" 3 "${rootfs_dir}" + "${SCRIPT_DIR}/verify_uefi.sh" "${rootfs_dir}/boot" "${esp_dir}" || \ + succeeded=0 + sudo umount "${rootfs_dir}" + + sudo umount "${esp_dir}" + + if [[ "${succeeded}" == "0" ]]; then + die "UEFI signature verification failed" + fi +} + # Sign an oci container with the given keys. # Args: CONTAINER KEY_DIR [OUTPUT_CONTAINER] sign_oci_container() { @@ -818,6 +872,8 @@ EOF verify_image_rootfs "${loop_rootfs}" + verify_uefi_signatures "${INPUT_IMAGE}" + # TODO(gauravsh): Check embedded firmware AU signatures. } @@ -854,19 +910,11 @@ update_legacy_bootloader() { local image="$1" local loop_kern="$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 + local esp_dir="$(mount_image_esp "${image}")" + if [[ -z "${esp_dir}" ]]; 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. - if ! _mount_image_partition_retry "${image}" "${esp_partnum}" \ - "${esp_dir}"; then + elif [[ "${esp_dir}" == "MOUNT_FAILED" ]]; then error "Could not mount EFI partition for updating legacy bootloader cfg." return 1 fi @@ -929,6 +977,7 @@ sign_image_file() { resign_firmware_payload "${output}" resign_android_image_if_exists "${output}" + sign_uefi_binaries "${output}" # We do NOT strip /boot for factory installer, since some devices need it to # boot EFI. crbug.com/260512 would obsolete this requirement. # diff --git a/scripts/image_signing/sign_uefi.sh b/scripts/image_signing/sign_uefi.sh new file mode 100755 index 00000000..4cef5a50 --- /dev/null +++ b/scripts/image_signing/sign_uefi.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# Copyright 2018 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. + +. "$(dirname "$0")/common.sh" + +set -e + +usage() { + cat <<EOF +Usage: $PROG /path/to/target/dir /path/to/keys/dir + +Sign UEFI binaries in the target directory. +EOF + if [[ $# -gt 0 ]]; then + error "$*" + exit 1 + fi + exit 0 +} + +sign_efi_file() { + local target="$1" + local temp_dir="$2" + local priv_key="$3" + local sign_cert="$4" + local verify_cert="$5" + if [[ -z "${verify_cert}" ]]; then + verify_cert="${sign_cert}" + fi + + info "Signing efi file ${target}" + sudo sbattach --remove "${target}" || true + local signed_file="${temp_dir}/$(basename "${target}")" + sbsign --key="${priv_key}" --cert="${sign_cert}" \ + --output="${signed_file}" "${target}" || warn "Cannot sign ${target}" + if [[ -f "${signed_file}" ]]; then + sudo cp -f "${signed_file}" "${target}" + sbverify --cert "${verify_cert}" "${target}" || die "Verification failed" + fi +} + +main() { + local target_dir="$1" + local key_dir="$2" + + if [[ $# -ne 2 ]]; then + usage "command takes exactly 2 args" + fi + + if ! type -P sbattach &>/dev/null; then + die "Skip signing UEFI binaries (sbattach not found)." + fi + if ! type -P sbsign &>/dev/null; then + die "Skip signing UEFI binaries (sbsign not found)." + fi + if ! type -P sbverify &>/dev/null; then + die "Skip signing UEFI binaries (sbverify not found)." + fi + + local bootloader_dir="${target_dir}/efi/boot" + local syslinux_dir="${target_dir}/syslinux" + local kernel_dir="${target_dir}" + + local verify_cert="${key_dir}/db/db.pem" + if [[ ! -f "$verify_cert" ]]; then + die "No verification cert: ${verify_cert}" + fi + + local sign_cert="${key_dir}/db/db.children/db_child.pem" + if [[ ! -f "${sign_cert}" ]]; then + die "No signing cert: ${sign_cert}" + fi + + local sign_key="${key_dir}/db/db.children/db_child.rsa" + if [[ ! -f "${sign_key}" ]]; then + die "No signing key: ${sign_key}" + fi + + local working_dir="$(make_temp_dir)" + + for efi_file in "${bootloader_dir}/"*".efi"; do + if [[ ! -f "${efi_file}" ]]; then + continue + fi + sign_efi_file "${efi_file}" "${working_dir}" \ + "${sign_key}" "${sign_cert}" "${verify_cert}" + done + + for syslinux_kernel_file in "${syslinux_dir}/vmlinuz."?; do + if [[ ! -f "${syslinux_kernel_file}" ]]; then + continue + fi + sign_efi_file "${syslinux_kernel_file}" "${working_dir}" \ + "${sign_key}" "${sign_cert}" "${verify_cert}" + done + + local kernel_file="$(readlink -f "${kernel_dir}/vmlinuz")" + if [[ -f "${kernel_file}" ]]; then + sign_efi_file "${kernel_file}" "${working_dir}" \ + "${sign_key}" "${sign_cert}" "${verify_cert}" + fi +} + +main "$@" diff --git a/scripts/image_signing/verify_uefi.sh b/scripts/image_signing/verify_uefi.sh new file mode 100755 index 00000000..959b5b8f --- /dev/null +++ b/scripts/image_signing/verify_uefi.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Copyright 2018 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. + +. "$(dirname "$0")/common.sh" + +set -e + +usage() { + cat <<EOF +Usage: $PROG /path/to/target/dir /path/to/esp/dir + +Verify signatures of UEFI binaries in the target directory. +EOF + if [[ $# -gt 0 ]]; then + error "$*" + exit 1 + fi + exit 0 +} + +main() { + local target_dir="$1" + local esp_dir="$2" + + if [[ $# -ne 2 ]]; then + usage "command takes exactly 1 args" + fi + + if ! type -P sbverify &>/dev/null; then + die "Cannot verify UEFI signatures (sbverify not found)." + fi + + local bootloader_dir="${target_dir}/efi/boot" + local syslinux_dir="${target_dir}/syslinux" + local kernel_dir="${target_dir}" + local gsetup_dir="${esp_dir}/EFI/Google/GSetup" + + if [[ ! -f "${gsetup_dir}/pk/pk.der" ]]; then + warn "No PK cert" + exit 0 + fi + + local db_cert_der="${gsetup_dir}/db/db.der" + if [[ ! -f "${db_cert_der}" ]]; then + warn "No DB cert" + exit 0 + fi + + local working_dir="$(make_temp_dir)" + local cert="${working_dir}/cert.pem" + openssl x509 -in "${db_cert_der}" -inform DER -out "${cert}" -outform PEM + + for efi_file in "${bootloader_dir}/"*".efi"; do + if [[ ! -f "${efi_file}" ]]; then + continue + fi + sbverify --cert "${cert}" "${efi_file}" || + die "Verification failed: ${efi_file}" + done + + for syslinux_kernel_file in "${syslinux_dir}/vmlinuz."?; do + if [[ ! -f "${syslinux_kernel_file}" ]]; then + continue + fi + sbverify --cert "${cert}" "${syslinux_kernel_file}" || + warn "Verification failed: ${syslinux_kernel_file}" + done + + local kernel_file="$(readlink -f "${kernel_dir}/vmlinuz")" + if [[ -f "${kernel_file}" ]]; then + sbverify --cert "${cert}" "${kernel_file}" || + warn "Verification failed: ${kernel_file}" + fi +} + +main "$@" |