diff options
author | mwberry | 2016-11-12 15:21:41 -0800 |
---|---|---|
committer | mwberry | 2016-11-12 18:11:54 -0800 |
commit | 5e3dbf7cc7110217239017879c49287d6cd633fa (patch) | |
tree | ba42fdc8303dfd1b1de043a2001836e54d090fd9 | |
parent | 490f95b5c795e82d636d6234cf79e068a191e2c9 (diff) | |
download | aur-5e3dbf7cc7110217239017879c49287d6cd633fa.tar.gz |
extract common functions from nannycam
-rwxr-xr-x | nannycam | 159 | ||||
-rwxr-xr-x | nannycam.functions | 172 |
2 files changed, 180 insertions, 151 deletions
@@ -4,155 +4,14 @@ set -u set -e # set -x -HASH=sha256 - -# Check if running outside the initramfs environment -if ! type resolve_device &>/dev/null; then - # Setup enough of the early userspace environment that resolve_device works - source /usr/lib/initcpio/init_functions - udevd_running=$(ps aux | grep udevd | grep -v grep | wc -l) - rootdelay=1 -fi - -usage () { - cat <<HELPEOF - nannycam -k keyfile -m hash -p hash [-e hash] - - -k Signing Key - -m Expected MBR hash - -p Expected Post-MBR Gap hash - -e Expected EFI Stub hash - - nannycam helps protect against Evil Maid attacks against encrypted boot partitions. - First, nannycam hashes the MBR, Post-MBR gap, and (optionally) the named EFI stub - and compares them against expected values. Next, nannycam uses a private key to - sign the current date and time. It is expected that this key be stored inside the - encrypted boot partition. The signature is displayed as a QR code so that another - device can verify that the signature is valid and the date/time signed is recent. - - If any of the hashes do not match, the boot process is paused. An error message is - displayed advising the user NOT to enter their root device encryption passphrase. - If the user feels there has been a misconfiguration, the user can continue the boot - sequence (potentially exposing their root encryption passphrase in the process). - - The user must type (in uppercase) YES to continue the boot sequence after verifying - the date/time signature. If the signature does not validate, it is possible that - the entire encrypted boot partition has been replaced with one that was crafted to - appear similar but might record and/or transmit the root encryption passphrase. - - Presumably, if the encryption of the boot partition is secure, then the key stored - inside the encrypted boot partition cannot be known by an attacker. This prevents - wholesale replacement of the entire boot partition. Hashes taken of the MBR, Post- - MBR Gap, and EFI Stub assist in protecting against attackers replacing them in an - attempt to record the boot partition's encryption passphrase and subsequently - extracting the private key material used to authenticate the boot partition. - - nannycam does not prevent all Evil Maid attacks. It is of course possible for state - actors to launch hardware-level attacks, but even less powerful adversaries may be - clever enough to thwart these protections. -HELPEOF -} - - -err_required_arg () { - echo "-$1 is a required option" >&2 - exit 1 -} +source nannycam.functions -hash_mismatch () { - (cat <<WARNEOF -***************************************************************** -* WARNING: Unexpected hash. Do NOT enter root device passphrase * -***************************************************************** - -There was a mismatch in the expected and actual hash values for -critical boot programs. Either a misconfiguration has occurred or -a malicious actor has modified one or more of these programs. It -is advised that you restore the MBR, Post MBR Gap, and (if using -EFI) the EFI Stub from secure backups. Do NOT enter your root -device passphrase unless you are certain this is a -misconfiguration. - -Hashing algorithm: $HASH -MBR (expected) $EXPECTED_MBR_HASH -MBR (actual) $ACTUAL_MBR_HASH -MBR Gap (expected) $EXPECTED_MBR_GAP_HASH -MBR Gap (actual) $ACTUAL_MBR_GAP_HASH -EFI Stub (expected) $EXPECTED_EFI_STUB_HASH -EFI Stub (actual) $ACTUAL_EFI_STUB_HASH - -WARNEOF -) >&2 - - local response="" - while [[ "$response" != "YES" ]]; do - read -p "Enter YES to continue booting (not recommended): " response - done -} - -determine_mbr_boot_device () { - local possibleDevices=$(parted -s -m -l \ - | sed -e 's/^$/\x00/' \ - | tr -d '\n' \ - | tr '\0' '\n' \ - | egrep ';([^:]*:){6}boot' \ - | cut -f 2 -d ';' \ - | cut -f 1 -d ':') - [ $(echo "$possibleDevices" | wc -l) -eq 1 ] \ - || ( echo "Expected exactly one partition with boot flag set, aborting." >&2; exit 5 ) - echo "$possibleDevices" -} - -check_mbr () { - local mbrDevice=$(determine_mbr_boot_device) - # dd if="$mbrDevice" of=/tmp/mbr.bin bs=512 count=1 - ACTUAL_MBR_HASH="$(openssl dgst -$HASH /tmp/mbr.bin | cut -f 2 -d ' ')" - # rm /tmp/mbr.bin - [[ "$EXPECTED_MBR_HASH" == "$ACTUAL_MBR_HASH" ]] -} - -check_mbr_gap () { - local mbrDevice=$(determine_mbr_boot_device) - # local part_start=$(parted -s -m "$mbrDevice" unit b print | egrep '^1:' | cut -f 2 -d ':' | tr -d 'Bb') - # local blocks=$(( part_start / 512 )) - # local check=$(( $blocks * 512 )) - # [ $part_start -eq $check ] || ( echo "Partition doesn't start at 512 byte boundary! Aborting." >&2; exit 3 ) - # dd if="$mbrDevice" of=/tmp/gap.bin bs=512 skip=1 count=$blocks - ACTUAL_MBR_GAP_HASH="$(openssl dgst -$HASH /tmp/gap.bin | cut -f 2 -d ' ')" - # rm /tmp/gap.bin - [[ "$EXPECTED_MBR_GAP_HASH" == "$ACTUAL_MBR_GAP_HASH" ]] -} - -check_already_mounted () { - local device="$1" - echo $(mount | grep "^$device on" | cut -f 3 -d ' ') -} +HASH=sha256 -check_efi_stub () { - # Don't bother checking efi stub if booting in MBR mode - if ! mount | grep efivarfs &>/dev/null; then - return 0 - fi - # Determine which EFI stub was used - local bootinfo="$(efibootmgr -v)" - local currentNum=$(echo "$bootinfo" | egrep '^BootCurrent:' | cut -f 2 -d ' ') - local current=$(echo "$bootinfo" | grep "^Boot$currentNum\*") - # TODO: Support other boot devices other than ESP System Partitions - local partitionAndPath=$( echo "$current" | \ - sed -nre 's_^.*HD\([0-9]+,GPT,([^,]{36}),[^)]+\)/File\(([^)]+)\)_\1\2_p') - local partitionUUID=$(echo "$partitionAndPath" | head -c 36 ) - # TODO: Determine how escaped paths work with efibootmgr - local path=$(echo "$partitionAndPath" | tail -c +37 | tr '\\' '/' ) - local mountDevice=$(resolve_device PARTUUID=$partitionUUID) - local mountPoint=$(check_already_mounted "$mountDevice") - if [ -z "$mountPoint" ]; then - mountPath="/tmp/efi" - mount $mountDevice $mountPoint - fi - ACTUAL_EFI_STUB_HASH=$(openssl dgst -$HASH "$mountPoint$path" | cut -f 2 -d ' ') - [[ "$EXPECTED_EFI_STUB_HASH" == "$ACTUAL_EFI_STUB_HASH" ]] -} +# Check if running outside the initramfs environment, setup env otherwise +ensure_initramfs_environment +# Parse options while getopts ":k:m:p:e:" opt; do case $opt in k) @@ -168,7 +27,7 @@ while getopts ":k:m:p:e:" opt; do EXPECTED_EFI_STUB_HASH="$OPTARG" ;; \?) - usage >&2 + nannycam_usage >&2 exit 1 ;; :) @@ -182,10 +41,8 @@ done [ -z ${EXPECTED_MBR_GAP_HASH:-} ] && err_required_arg p [ -z ${EXPECTED_EFI_STUB_HASH:-} ] && err_required_arg e -if [[ "0" != "$(id -u)" ]]; then - echo "Must be run as root." >&2 - exit 4 -fi +# Can't access block devices unless root +assert_root ACTUAL_MBR_HASH="not checked" ACTUAL_MBR_GAP_HASH="not checked" diff --git a/nannycam.functions b/nannycam.functions new file mode 100755 index 000000000000..8db1d6191ffc --- /dev/null +++ b/nannycam.functions @@ -0,0 +1,172 @@ +# +# Shared Functions for nannycam and the associated install hook +# + +ensure_initramfs_environment() { + # Check if running outside the initramfs environment + if ! type resolve_device &>/dev/null; then + # Setup enough of the early userspace environment that resolve_device works + source /usr/lib/initcpio/init_functions + udevd_running=$(ps aux | grep udevd | grep -v grep | wc -l) + rootdelay=1 + fi +} + +nannycam_usage () { + cat <<HELPEOF + nannycam -k keyfile -m hash -p hash [-e hash] + + -k Signing Key + -m Expected MBR hash + -p Expected Post-MBR Gap hash + -e Expected EFI Stub hash + + nannycam helps protect against Evil Maid attacks against encrypted boot partitions. + First, nannycam hashes the MBR, Post-MBR gap, and (optionally) the named EFI stub + and compares them against expected values. Next, nannycam uses a private key to + sign the current date and time. It is expected that this key be stored inside the + encrypted boot partition. The signature is displayed as a QR code so that another + device can verify that the signature is valid and the date/time signed is recent. + + If any of the hashes do not match, the boot process is paused. An error message is + displayed advising the user NOT to enter their root device encryption passphrase. + If the user feels there has been a misconfiguration, the user can continue the boot + sequence (potentially exposing their root encryption passphrase in the process). + + The user must type (in uppercase) YES to continue the boot sequence after verifying + the date/time signature. If the signature does not validate, it is possible that + the entire encrypted boot partition has been replaced with one that was crafted to + appear similar but might record and/or transmit the root encryption passphrase. + + Presumably, if the encryption of the boot partition is secure, then the key stored + inside the encrypted boot partition cannot be known by an attacker. This prevents + wholesale replacement of the entire boot partition. Hashes taken of the MBR, Post- + MBR Gap, and EFI Stub assist in protecting against attackers replacing them in an + attempt to record the boot partition's encryption passphrase and subsequently + extracting the private key material used to authenticate the boot partition. + + nannycam does not prevent all Evil Maid attacks. It is of course possible for state + actors to launch hardware-level attacks, but even less powerful adversaries may be + clever enough to thwart these protections. +HELPEOF +} + + +err_required_arg () { + echo "-$1 is a required option" >&2 + exit 1 +} + +hash_mismatch () { + (cat <<WARNEOF +***************************************************************** +* WARNING: Unexpected hash. Do NOT enter root device passphrase * +***************************************************************** + +There was a mismatch in the expected and actual hash values for +critical boot programs. Either a misconfiguration has occurred or +a malicious actor has modified one or more of these programs. It +is advised that you restore the MBR, Post MBR Gap, and (if using +EFI) the EFI Stub from secure backups. Do NOT enter your root +device passphrase unless you are certain this is a +misconfiguration. + +Hashing algorithm: $HASH +MBR (expected) $EXPECTED_MBR_HASH +MBR (actual) $ACTUAL_MBR_HASH +MBR Gap (expected) $EXPECTED_MBR_GAP_HASH +MBR Gap (actual) $ACTUAL_MBR_GAP_HASH +EFI Stub (expected) $EXPECTED_EFI_STUB_HASH +EFI Stub (actual) $ACTUAL_EFI_STUB_HASH + +WARNEOF +) >&2 + + local response="" + while [[ "$response" != "YES" ]]; do + read -p "Enter YES to continue booting (not recommended): " response + done +} + +determine_mbr_boot_device () { + local possibleDevices=$(parted -s -m -l \ + | sed -e 's/^$/\x00/' \ + | tr -d '\n' \ + | tr '\0' '\n' \ + | egrep ';([^:]*:){6}boot' \ + | cut -f 2 -d ';' \ + | cut -f 1 -d ':') + [ $(echo "$possibleDevices" | wc -l) -eq 1 ] \ + || ( echo "Expected exactly one partition with boot flag set, aborting." >&2; exit 5 ) + echo "$possibleDevices" +} + +hash_mbr () { + local mbrDevice=$(determine_mbr_boot_device) + # dd if="$mbrDevice" of=/tmp/mbr.bin bs=512 count=1 + ACTUAL_MBR_HASH="$(openssl dgst -$HASH /tmp/mbr.bin | cut -f 2 -d ' ')" + # rm /tmp/mbr.bin +} + +check_mbr () { + hash_mbr + [[ "$EXPECTED_MBR_HASH" == "$ACTUAL_MBR_HASH" ]] +} + + +hash_mbr_gap () { + local mbrDevice=$(determine_mbr_boot_device) + # local part_start=$(parted -s -m "$mbrDevice" unit b print | egrep '^1:' | cut -f 2 -d ':' | tr -d 'Bb') + # local blocks=$(( part_start / 512 )) + # local check=$(( $blocks * 512 )) + # [ $part_start -eq $check ] || ( echo "Partition doesn't start at 512 byte boundary! Aborting." >&2; exit 3 ) + # dd if="$mbrDevice" of=/tmp/gap.bin bs=512 skip=1 count=$blocks + ACTUAL_MBR_GAP_HASH="$(openssl dgst -$HASH /tmp/gap.bin | cut -f 2 -d ' ')" + # rm /tmp/gap.bin +} + +check_mbr_gap () { + hash_mbr_gap + [[ "$EXPECTED_MBR_GAP_HASH" == "$ACTUAL_MBR_GAP_HASH" ]] +} + +check_already_mounted () { + local device="$1" + echo $(mount | grep "^$device on" | cut -f 3 -d ' ') +} + +hash_efi_stub () { + # Don't bother checking efi stub if booting in MBR mode + if ! mount | grep efivarfs &>/dev/null; then + return 0 + fi + # Determine which EFI stub was used + local bootinfo="$(efibootmgr -v)" + local currentNum=$(echo "$bootinfo" | egrep '^BootCurrent:' | cut -f 2 -d ' ') + local current=$(echo "$bootinfo" | grep "^Boot$currentNum\*") + # TODO: Support other boot devices other than ESP System Partitions + local partitionAndPath=$( echo "$current" | \ + sed -nre 's_^.*HD\([0-9]+,GPT,([^,]{36}),[^)]+\)/File\(([^)]+)\)_\1\2_p') + local partitionUUID=$(echo "$partitionAndPath" | head -c 36 ) + # TODO: Determine how escaped paths work with efibootmgr + local path=$(echo "$partitionAndPath" | tail -c +37 | tr '\\' '/' ) + local mountDevice=$(resolve_device PARTUUID=$partitionUUID) + local mountPoint=$(check_already_mounted "$mountDevice") + if [ -z "$mountPoint" ]; then + mountPath="/tmp/efi" + mount $mountDevice $mountPoint + fi + ACTUAL_EFI_STUB_HASH=$(openssl dgst -$HASH "$mountPoint$path" | cut -f 2 -d ' ') +} + +check_efi_stub () { + hash_efi_stub + [[ "$EXPECTED_EFI_STUB_HASH" == "$ACTUAL_EFI_STUB_HASH" ]] +} + +assert_root() { + if [[ "0" != "$(id -u)" ]]; then + echo "Must be run as root." >&2 + exit 4 + fi +} |