summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authormwberry2016-11-12 15:21:41 -0800
committermwberry2016-11-12 18:11:54 -0800
commit5e3dbf7cc7110217239017879c49287d6cd633fa (patch)
treeba42fdc8303dfd1b1de043a2001836e54d090fd9
parent490f95b5c795e82d636d6234cf79e068a191e2c9 (diff)
downloadaur-5e3dbf7cc7110217239017879c49287d6cd633fa.tar.gz
extract common functions from nannycam
-rwxr-xr-xnannycam159
-rwxr-xr-xnannycam.functions172
2 files changed, 180 insertions, 151 deletions
diff --git a/nannycam b/nannycam
index 6afe2af07170..451a084bec56 100755
--- a/nannycam
+++ b/nannycam
@@ -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
+}