diff options
author | mwberry | 2016-11-12 15:21:41 -0800 |
---|---|---|
committer | mwberry | 2016-11-12 18:11:34 -0800 |
commit | 490f95b5c795e82d636d6234cf79e068a191e2c9 (patch) | |
tree | cc2b14e22ab510bebe369c10f1d6701f65cec88b | |
parent | 544a658ccf5092361e62d98e2512e4c8e42c7c20 (diff) | |
download | aur-490f95b5c795e82d636d6234cf79e068a191e2c9.tar.gz |
nannycam functional, parts of install hook
-rw-r--r-- | .SRCINFO | 1 | ||||
-rw-r--r-- | PKGBUILD | 2 | ||||
-rw-r--r-- | config | 46 | ||||
-rwxr-xr-x | install | 79 | ||||
-rwxr-xr-x | nannycam | 100 |
5 files changed, 211 insertions, 17 deletions
@@ -5,6 +5,7 @@ pkgbase = mkinitcpio-nannycam url = https://wiki.archlinux.org/index.php/User:Mwberry/mkinitcpio-nannycam arch = any license = GPL2 + depends = parted depends = openssl depends = qrencode depends = bash @@ -6,7 +6,7 @@ pkgdesc="Assists protecting encrypted boot partitions from (some) Evil Maid atta url="https://wiki.archlinux.org/index.php/User:Mwberry/mkinitcpio-nannycam" arch=('any') license=('GPL2') -depends=('openssl' 'qrencode' 'bash' 'coreutils') +depends=('parted' 'openssl' 'qrencode' 'bash' 'coreutils') optdepends=() makedepends=() conflicts=() diff --git a/config b/config new file mode 100644 index 000000000000..6485cb52a1c8 --- /dev/null +++ b/config @@ -0,0 +1,46 @@ +############################### +# nannycam configuration file +# +# (this is an ash-shell snippet) +################################ + +# The authentication key is what is used to prove +# that the encrypted boot partition has not been +# completely replaced with one created by the attacker + +# Where to store the file in the initramfs +auth_key_file=/boot_partition_auth.pem + +# RSA key size, in bits +# Note: The size of the key determines the size of the +# signature. The size of the signature determines the +# size of the QR code that will be printed to the terminal. +# Pick the largest key size that fits on your monitor +auth_key_length=4096 + + +# Hashes of important boot programs + +# The hashing algorithm to use +hash_alg=sha256 + +# Expected hash values +# Note: These are calculated for you each time mkinitcpio +# runs. The only time you would want to uncomment these is +# if the logic in the install hook incorrectly detects your +# configuration and you want to override the logic. + +# The hash of the MBR +# (first 512 bytes of disk housing partition with boot flag set) +# expected_mbr_hash= + +# The hash of the Post-MBR Gap +# (bytes from the end of the MBR to the start of the first partition) +# expected_mbr_gap_hash= + +# The hash of the EFI stub used to boot +# (hash of the file invoked by the UEFI firmware, likely /EFI/grub/grubx64.efi) +# Note: Only checked when booting via UEFI +# Note: MBR and Post MBR Gap are still checked when booting via UEFI +# expected_efi_stub_hash= + diff --git a/install b/install new file mode 100755 index 000000000000..a9db86089966 --- /dev/null +++ b/install @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e +set -u + +# This script is expected to be called from mkinitcpio, if not... +if [ -z ${BUILDROOT:-} ]; then + # ...then mock out enough of the environment to enable testing + saveOpts=$(set +o | egrep 'xtrace|errexit|nounset') + saveGlob=$(shopt -p | grep extglob) + shopt -s extglob + set +e + set +u + set +x + . "/usr/lib/initcpio/functions" + BUILDROOT=$(initialize_buildroot $(uname -r) $(mktemp -d --tmpdir mkinitcpio.XXXXXX)) + _optgenimg=$(find /boot -name '*.img' 2>/dev/null | head -n 1) + _optquiet=1 + eval "$saveOpts" + eval "$saveGlob" +fi + +assert_ephemeral() { + fsType=$(df "$1" | tail -n 1 | cut -f 1 -d ' ') + if [[ "tmpfs" != "$fsType" ]]; then + (cat <<TMPWARN +"$1" is not on an ephemeral file system. Cowardly aborting in order to avoid +leaking the private key that will authenticate the encrypted boot device. +TMPWARN +) >&2 + exit 1 + fi +} + +assert_boot_part_encrypted() { + fsMnt=$(df "$_optgenimg" | tail -n 1 | egrep -o ' [^ ]+$' | tail -c +2) + isCrypt=$(lsblk -ro TYPE,MOUNTPOINT | egrep "$fsMnt$" | egrep '^crypt' | wc -l) + if [ ! $isCrypt -eq 1 ]; then + (cat <<DESTWARN +Destination location for the initramfs image is not on an encrypted device. +The nannycam software can only protect against Evil Maid style attacks if +the initramfs (and therefore the authentication key) is stored inside an +encrypted boot partition. Cowardly aborting in order to avoid leaking the +private key. +Image location: $_optgenimg +DESTWARN +) >&2 + exit 2 + fi +} + +assert_ephemeral "$BUILDROOT" +assert_ephemeral "/tmp" +assert_boot_part_encrypted + +if [ 0 -ne $(id -u) ]; then + echo "Must be running as root" >&2 + exit 3 +fi + +KEYFILE="/tmp/boot_partition_auth.pem" +PUBFILE="/tmp/boot_partition_auth.pub" + +touch "$KEYFILE" +chmod 700 "$KEYFILE" + +openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -out "$KEYFILE" 2> /dev/null +openssl rsa -pubout -out "$PUBFILE" -outform DER -in "$KEYFILE" 2> /dev/null + +add_file "$KEYFILE" + +shred -uf "$KEYFILE" + +echo "Scan the following public key into your verification device" +cat "$PUBFILE" | qrencode -8 -t ANSIUTF8 -m 1 +read -p "Press ENTER to continue..." pause + +rm "$PUBFILE" + @@ -4,6 +4,16 @@ 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] @@ -57,15 +67,19 @@ hash_mismatch () { 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 this 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. - - Expected Hash Actual Hash -MBR $EXPECTED_MBR_HASH $ACTUAL_MBR_HASH -MBR Gap $EXPECTED_MBR_GAP_HASH $ACTUAL_MBR_GAP_HASH -EFI Stub $EXPECTED_EFI_STUB_HASH $ACTUAL_EFI_STUB_HASH +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 @@ -76,16 +90,67 @@ WARNEOF 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 () { - echo "TODO: Calculate MBR HASH" + 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 () { - echo "TODO: Calculate MBR Gap Hash" + 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 ' ') } check_efi_stub () { - echo "TODO: Check EFI Stub hash" + # 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" ]] } while getopts ":k:m:p:e:" opt; do @@ -117,6 +182,11 @@ 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 + ACTUAL_MBR_HASH="not checked" ACTUAL_MBR_GAP_HASH="not checked" ACTUAL_EFI_STUB_HASH="not checked" @@ -126,12 +196,10 @@ if [ ! -f "$KEYFILE" ]; then exit 2 fi -check_mbr || hash_mismatch -check_mbr_gap || hash_mismatch -check_efi_stub || hash_mismatch +check_mbr && check_mbr_gap && check_efi_stub || hash_mismatch DATE_TIME="$(date +%s)" -echo -n "$DATE_TIME" | openssl pkeyutl -inkey "$KEYFILE" -sign | qrencode -t UTF8 -m0 +echo -n "$DATE_TIME" | openssl pkeyutl -inkey "$KEYFILE" -sign | qrencode -8 -t ANSIUTF8 -m1 echo "$DATE_TIME" response="" |