summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authormwberry2016-11-12 15:21:41 -0800
committermwberry2016-11-12 18:11:34 -0800
commit490f95b5c795e82d636d6234cf79e068a191e2c9 (patch)
treecc2b14e22ab510bebe369c10f1d6701f65cec88b
parent544a658ccf5092361e62d98e2512e4c8e42c7c20 (diff)
downloadaur-490f95b5c795e82d636d6234cf79e068a191e2c9.tar.gz
nannycam functional, parts of install hook
-rw-r--r--.SRCINFO1
-rw-r--r--PKGBUILD2
-rw-r--r--config46
-rwxr-xr-xinstall79
-rwxr-xr-xnannycam100
5 files changed, 211 insertions, 17 deletions
diff --git a/.SRCINFO b/.SRCINFO
index 8aaa3dbd8cc5..fd97f6f5436b 100644
--- a/.SRCINFO
+++ b/.SRCINFO
@@ -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
diff --git a/PKGBUILD b/PKGBUILD
index 5c4fac1e5edd..12d11c008b40 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -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"
+
diff --git a/nannycam b/nannycam
index 064190f2aab5..6afe2af07170 100755
--- a/nannycam
+++ b/nannycam
@@ -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=""