Ver Fonte

Replaced early boot SquashFS hashing with DM-Verity

Prior to DM-Verity setup, key superblock properties are verified
(including hash algorithm).

The "cdroot_hash" boot parameter is now prefixed with a 4096-bytes
blocks offset from the end of SquashFS image, where DM-Verity
data starts.

Also removed qemulate.sh script, since supporting direct SquashFS
mount would become too cumbersome for such a rarely used feature.
Maxim Kammerer há 12 anos atrás
pai
commit
76f43f8fe0

+ 2 - 0
doc/changelog.txt

@@ -3,6 +3,7 @@
   * Release naming convention is YYYY.MM from now on
 
   * Kernel 3.9.9 with module signing and BFQ I/O scheduler
+  * DM-Verity for faster SquashFS image verification
   * Support for installation to GPT media
   * Added support for exFAT filesystem
   * Better support for QEMU mouse virtualization
@@ -26,6 +27,7 @@
   * Replaced XChat with HexChat
 
   * OVA image creation is fully integrated into build process
+  * Removed qemulate.sh script
 
 
 2012-09-01

+ 1 - 1
doc/info.txt

@@ -81,7 +81,7 @@ Kernel
   + RSA-4096 / SHA-256 module signing
 
 SquashFS image
-  + SHA-256 verification in initramfs
+  + SHA-256 DM-Verity in initramfs
 
 LUKS
   + AES-256 / XTS (w/ double key size)

+ 25 - 29
src/etc/init.d/liberte

@@ -51,32 +51,30 @@ ntfsflags=noatime,nosuid,nodev,compression,usermapping=/etc/ntfs-3g.map
 
 start() {
     mediaro=1
-    if mountpoint -q ${boot}; then
-        # Determine boot device and its label
-        mediadev=/dev/block/`mountpoint -d ${boot}`
-        medialabel=`udevadm info -q property -p ${mediadev} | sed -n 's/^ID_FS_LABEL=//p'`
-
-        if [ -n "${medialabel}" ]; then
-            # Can result in duplicate autofs entries if label is later changed
-            # to that of other media, and this service is then restarted
-            ebegin "Creating ${boot} -> /media/${medialabel} alias"
-            sed -i "s@^[^#].*\( [^ ]* :${boot}\$\)@\"${medialabel}\"\1@" ${autos}
-            eend $?
-        fi
+    # Determine boot device and its label
+    mediadev=/dev/block/`mountpoint -d ${boot}`
+    medialabel=`udevadm info -q property -p ${mediadev} | sed -n 's/^ID_FS_LABEL=//p'`
+
+    if [ -n "${medialabel}" ]; then
+        # Can result in duplicate autofs entries if label is later changed
+        # to that of other media, and this service is then restarted
+        ebegin "Creating ${boot} -> /media/${medialabel} alias"
+        sed -i "s@^[^#].*\( [^ ]* :${boot}\$\)@\"${medialabel}\"\1@" ${autos}
+        eend $?
+    fi
 
 
-        # Remount media root read-write (for non-loop/cd/dvd)
-        get_bootparam 'readonly' || mediaro=`blockdev --getro ${mediadev} 2>/dev/null` || mediaro=1
-        if [ ${mediaro} = 0 ]; then
-            ebegin Remounting ${boot} read-write
-            mount -o remount,rw ${boot} && /usr/bin/test -w ${boot}
-            mediaro=$?
-            eend ${mediaro}
-        elif [ ! -e ${mediadev} ]; then
-            ewarn Boot media device lost, not remounting
-        else
-            ewarn Boot media is read-only, not remounting
-        fi
+    # Remount media root read-write (for non-cd/dvd)
+    get_bootparam 'readonly' || mediaro=`blockdev --getro ${mediadev} 2>/dev/null` || mediaro=1
+    if [ ${mediaro} = 0 ]; then
+        ebegin Remounting ${boot} read-write
+        mount -o remount,rw ${boot} && /usr/bin/test -w ${boot}
+        mediaro=$?
+        eend ${mediaro}
+    elif [ ! -e ${mediadev} ]; then
+        ewarn Boot media device lost, not remounting
+    else
+        ewarn Boot media is read-only, not remounting
     fi
 
 
@@ -223,9 +221,7 @@ stop() {
 
     # Remount media root read-only
     # (apparently, does not conflict with write cache)
-    if mountpoint -q ${boot}; then
-        ebegin Remounting ${boot} read-only
-        mount -o remount,ro ${boot}
-        eend $?
-    fi
+    ebegin Remounting ${boot} read-only
+    mount -o remount,ro ${boot}
+    eend $?
 }

+ 4 - 8
src/etc/init.d/lockdown

@@ -36,14 +36,10 @@ start() {
 
 
     # Arm the poweroff watchdog
-    if mountpoint -q ${boot}; then
-        ebegin Arming power-off on boot media removal
-        mediadev=/dev/block/`mountpoint -d ${boot}`
-        start-stop-daemon -S -p ${pidfile} -bm -x ${launcher} -- ${mediadev}
-        eend $?
-    else
-        ewarn Skipping boot media removal watchdog
-    fi
+    ebegin Arming power-off on boot media removal
+    mediadev=/dev/block/`mountpoint -d ${boot}`
+    start-stop-daemon -S -p ${pidfile} -bm -x ${launcher} -- ${mediadev}
+    eend $?
 
 
     # Unblock [wifi wwan], block [bluetooth wimax uwb gps fm]

+ 1 - 4
src/etc/init.d/spindown

@@ -13,10 +13,7 @@ depend() {
 }
 
 start() {
-    mediadev=none
-    if mountpoint -q ${boot}; then
-        mediadev=$(readlink -f /dev/block/$(mountpoint -d ${boot}))
-    fi
+    mediadev=$(readlink -f /dev/block/$(mountpoint -d ${boot}))
 
     # Put ATA/SCSI drives into quiet mode
     # /dev/disk/by-path does not cover all physical devices (some lack ID_PATH)

+ 1 - 0
src/etc/portage/package.accept_keywords

@@ -6,6 +6,7 @@
 =sys-boot/grub-2.00*:2
 =app-cdr/cdrtools-3.01_alpha*
 =sys-boot/efibootmgr-0.5.4*
+=sys-fs/cryptsetup-1.6.2*
 
 # Network
 =net-wireless/reaver-1.4*

+ 6 - 0
src/etc/portage/package.use/all

@@ -124,3 +124,9 @@ sys-apps/man-pages              -linguas_*
 sys-apps/groff                  -linguas_*
 app-text/ghostscript-gpl        -linguas_*
 media-fonts/terminus-font       quote ru-dv ru-i
+
+# Static libraries (for initramfs cryptsetup)
+sys-apps/util-linux             static-libs
+sys-libs/e2fsprogs-libs         static-libs
+dev-libs/popt                   static-libs
+dev-libs/libgpg-error           static-libs

+ 4 - 0
src/etc/portage/package.use/temporary

@@ -6,3 +6,7 @@ dev-libs/libxml2                python
 
 # net-libs/libsoup circular dependency
 net-libs/libproxy               -networkmanager
+
+# initramfs cryptsetup
+sys-fs/cryptsetup               static      -udev  kernel -gcrypt
+sys-fs/lvm2                     static-libs -udev

+ 4 - 4
src/root/config/grub.cfg

@@ -1,9 +1,9 @@
 ## Variables:
-## VERSION:     distribution version                     (replaced in gen-efi)
-## CONSOLEFONT: PFF2 gfxterm font                        (replaced in gen-efi)
-## FSHASH:      SquashFS image hexadecimal SHA-256 hash  (replaced in mk-boot)
+## VERSION:     distribution version                        (replaced in gen-efi)
+## CONSOLEFONT: PFF2 gfxterm font                           (replaced in gen-efi)
+## FSHASH:      SquashFS DM-Verity hexadecimal SHA-256 hash (replaced in mk-boot)
 ##
-## grub.mf:     kernel+font SHA-256 checksums             (created in mk-boot)
+## grub.mf:     kernel+font SHA-256 checksums                (created in mk-boot)
 ##
 # Regular kernel parameters:
 #     (see syslinux.cfg)

+ 5 - 5
src/root/config/syslinux.cfg

@@ -1,15 +1,15 @@
 ## Variables
-## VERSION:     distribution version                     (replaced in gen-syslinux)
-## CONSOLEFONT: psf(u) console font                      (replaced in gen-syslinux)
-## FSHASH:      SquashFS image hexadecimal SHA-256 hash  (replaced in mk-boot)
+## VERSION:     distribution version                        (replaced in gen-syslinux)
+## CONSOLEFONT: psf(u) console font                         (replaced in gen-syslinux)
+## FSHASH:      SquashFS DM-Verity hexadecimal SHA-256 hash (replaced in mk-boot)
 ##
-## Encoding:    CP437                                  (translated in gen-syslinux)
+## Encoding:    CP437                                     (translated in gen-syslinux)
 ##
 # Liberté-specific kernel parameters (all are optional):
 #     cdroot_type={auto,vfat,ext4,iso9660,hfsplus,squashfs} - boot media filesystem type
 #     cdroot_flags=...   - boot media mount flags
 #     cdroot=/dev/xxx    - boot media device (can be a glob pattern)
-#     cdroot_hash=<hex>  - verify given SquashFS image hexadecimal SHA-256 hash
+#     cdroot_hash=<blks>:<hex> - SquashFS image DM-Verity hexadecimal SHA-256 root hash
 #     loop=...           - path to SquashFS image on boot media
 #     debug              - pause initramfs before mount probing and before booting
 #     readonly           - set read-only access for boot media device (no OTFE)

+ 0 - 61
src/root/dist/qemulate.sh

@@ -1,61 +0,0 @@
-#!/bin/sh
-set -e
-
-# TODO: add "-device virtio-rng" once it is supported by QEMU
-
-title="Virtualized Liberté (no persistence)"
-mem=192M
-bootdir=`dirname $0`/boot
-
-cdrom="if=virtio,format=raw,media=cdrom,aio=native,cache=none"
-
-params="loop=/ cdroot_hash=FSHASH quiet loglevel=4"
-
-export QEMU_AUDIO_DRV=alsa
-
-
-if [ -n "$1" ]; then
-    bootdir="$1"
-    shift
-fi
-
-if [ ! -d "${bootdir}" ]; then
-    echo "Error:  ${bootdir} not found (already virtualized?)"
-    echo "Format: $0 [bootdir [extra kernel params ...]]"
-    exit 1
-fi
-
-
-if type qemu-kvm 1>/dev/null 2>&1; then
-    qemu=qemu-kvm
-
-    if [ ! -e /dev/kvm ]; then
-        echo "Warning: no KVM driver has been loaded"
-        vmflag=`sed -n 's/^flags\>.*\<\(vmx\|svm\)\>.*$/\1/p' /proc/cpuinfo | head -n 1`
-
-        if [ "${vmflag}" = vmx ]; then
-            echo "Intel VT detected, loading KVM-Intel driver"
-            sudo -n modprobe kvm-intel
-        elif [ "${vmflag}" = svm ]; then
-            echo "AMD-V detected, loading KVM-AMD driver"
-            sudo -n modprobe kvm-amd
-        fi
-
-        if [ ! -e /dev/kvm ]; then
-            qemu="${qemu} -no-kvm -cpu qemu32"
-        fi
-    fi
-elif type qemu 1>/dev/null 2>&1; then
-    qemu=qemu
-else
-    echo "QEMU/QEMU-KVM not found"
-    exit 1
-fi
-
-
-exec ${qemu} -name "${title}" -m ${mem} -nodefaults          \
-             -sdl -balloon virtio -vga cirrus -monitor vc    \
-             -soundhw es1370 -net nic,model=virtio -net user \
-             -kernel "${bootdir}/kernel-x86.zi"              \
-             -drive file="${bootdir}/root-x86.sfs,${cdrom}"  \
-             -append "`echo ${params} $*`"

+ 4 - 5
src/root/helpers/mk-boot

@@ -5,7 +5,7 @@ cdroot=/mnt/boot/cdroot
 live=/mnt/live
 
 ziimage=${cdroot}/liberte/boot/kernel-x86.zi
-sqimage=${cdroot}/liberte/boot/root-x86.sfs
+sqhash=${live}/tmp/transient/verity.fshash
 
 efisbpfx=/usr/local/addons/secureboot/Liberte-SecureBoot
 
@@ -26,11 +26,10 @@ find ${cdroot}/liberte \( -name '*.txt' -o -name '*.bat' -o -name '*.cfg' \) \
 
 echo "Adapting Syslinux and GRUB configuration"
 
-fshash=`sha256sum ${sqimage}`
-sed -i "s/FSHASH/${fshash%% *}/"                  \
+fshash=`cat ${sqhash}`
+sed -i "s/FSHASH/${fshash}/"                      \
     ${cdroot}/liberte/boot/syslinux/syslinux.cfg  \
-    ${cdroot}/liberte/boot/grub/grub.cfg          \
-    ${cdroot}/liberte/qemulate.sh
+    ${cdroot}/liberte/boot/grub/grub.cfg
 
 sha256sum ${cdroot}/liberte/boot/grub/* ${ziimage}  \
     | sed "/\<grub\.\(cfg\|mf\)\>/d; s:${cdroot}::" \

+ 53 - 0
src/root/helpers/mk-squashfs

@@ -0,0 +1,53 @@
+#!/bin/sh -e
+
+# Variables
+helpdir=${HOME}/helpers
+rootfspfx=${HOME}/config/rootfs
+
+live=/mnt/live
+cdroot=/mnt/boot/cdroot
+
+sqimage=${cdroot}/liberte/boot/root-x86.sfs
+sqsort=${live}/tmp/transient/pkg/squashfs.sort
+sqhash=${live}/tmp/transient/verity
+
+hashalg=sha256
+
+
+# NOTE: POSIX ACLs are not supported by SquashFS
+#       Non-PaX user xattrs are not supported by tmpfs
+mksquashfs ${live} ${sqimage}                \
+    -noappend -no-progress -no-exports       \
+    -always-use-fragments -comp xz -Xbcj x86 \
+    -sort ${sqsort} -ef ${rootfspfx}.ignore
+chmod go= ${sqimage}
+
+
+# Append DM-Verity data
+sqsize=`du -b ${sqimage} | cut -f1`
+${helpdir}/veritywrapper format --hash=${hashalg} --hash-offset=${sqsize} ${sqimage} ${sqimage} > ${sqhash}
+
+
+# Verify superblock properties (same verification in initramfs)
+hashdesc=`sed -n 's/^.*:[[:blank:]]*//; 3p; 5,7p' ${sqhash} | tr '\n' :`
+if [ "${hashdesc}" != 1:4096:4096:${hashalg}: ]; then
+    echo "Unexpected hash descriptor: ${hashdesc}"
+    exit 1
+fi
+
+
+# Build FSHASH string
+roothash=`tail -1 ${sqhash} | sed 's/^.*:[[:blank:]]*//'`
+hashblks=`du -b ${sqimage} | cut -f1`
+hashblks=$(((hashblks - sqsize) / 4096))
+
+
+# Verify SquashFS image wrt. DM-Verity data
+sqsize=`du -b ${sqimage} | cut -f1`
+sqsize=$((sqsize - hashblks * 4096))
+
+${helpdir}/veritywrapper verify --hash-offset=${sqsize} ${sqimage} ${sqimage} ${roothash}
+
+
+# Write FSHASH string
+echo ${hashblks}:${roothash} > ${sqhash}.fshash

+ 6 - 0
src/root/helpers/veritywrapper

@@ -0,0 +1,6 @@
+#!/bin/sh -e
+# Run non-initramfs veritysetup (does not use kernel for hashing)
+
+live=/mnt/live
+
+LD_PRELOAD=${live}/usr/lib/libcryptsetup.so exec ${live}/sbin/veritysetup "$@"

+ 52 - 35
src/root/initrd/init

@@ -76,7 +76,7 @@ good_msg 'Loading modules'
 
 # Specify required filesystem modules (no autoloading on mount)
 # Load EHCI unconditionally, otherwise USB 1.1 might be forced if OHCI/UHCI comes up first
-force_load="loop ${force_load_fs} ehci-hcd"
+force_load="${force_load_fs} ehci-hcd"
 
 oldmods=
 newmods=" ${force_load} "
@@ -174,56 +174,73 @@ done
 
 # Setup the loopback mounts
 verifyroot() {
-    local image="$1"
+    local imgdev="$1"
 
     if [ -n "${param_cdroot_hash}" ]; then
-        good_msg 'Verifying filesystem image ...'
-        sfshash=`sha256sum "${image}"`
-        param_cdroot_hash=`echo "${param_cdroot_hash}" | tr A-F a-f`
+        good_msg 'Setting up DM-Verity for filesystem image'
 
-        [ "${param_cdroot_hash}" = "${sfshash%% *}" ]
-        test_success "verify filesystem image hash"
+        # Load required modules (no module autoloading)
+        modprobe -b dm_verity
+        modprobe -b algif_hash
+        modprobe -b sha1_generic
+
+        [ "${param_cdroot_hash%:*}" -gt 0 -a "${param_cdroot_hash%:*}" != "${param_cdroot_hash}" ] 2>/dev/null
+        test_success 'parse DM-Verity hash descriptor'
+
+        sfsbytes=`blockdev --getsize64 ${imgdev}`
+        sfsoffset=$((sfsbytes - ${param_cdroot_hash%:*} * 4096))
+        hashdesc=`veritysetup dump --hash-offset=${sfsoffset} ${imgdev} | sed -n 's/^.*:[[:blank:]]*//; 3p; 5,7p' | tr '\n' :`
+
+        [ "${hashdesc}" = 1:4096:4096:sha256: ]
+        test_success 'verify DM-Verity superblock'
+
+        veritysetup create --hash-offset=${sfsoffset} rootfs ${imgdev} ${imgdev} "${param_cdroot_hash#*:}"
+        test_success 'setup DM-Verity mapping'
+
+        veritysetup status rootfs | grep -qs '^[[:blank:]]*status:[[:blank:]]*verified$'
+        test_success 'setup DM-Verity mapping with given root hash'
+
+        # Remove modules used only for veritysetup's crypto backend init
+        modprobe -r sha1_generic
+        modprobe -r algif_hash
+
+        rootdev=/dev/mapper/rootfs
     else
         warn_msg 'Skipping filesystem image verification'
     fi
 }
 
-if [ "${param_loop}" != / ]; then
-    rootimg=${sboot}"${param_loop}"
-
-    # Let 'notoram' override 'toram' enforcement for CDs
-    [ -z "${param_notoram}" ] || param_toram=
-
-    if [ -n "${param_toram}" ]; then
-        good_msg 'Copying filesystem image to RAM ...'
-        rootimgram=${srwroot}/cache/root.sfs
+# Let 'notoram' override 'toram' enforcement for CDs
+rootimg=${sboot}"${param_loop}"
+if [ -n "${param_toram}" -a -z "${param_notoram}" ]; then
+    good_msg 'Copying filesystem image to RAM ...'
+    rootimgram=${srwroot}/cache/root.sfs
 
-        mkdir -m 700  ${srwroot}/cache
-        cp "${rootimg}" ${rootimgram} || rm ${rootimgram}
-        rootimg=${rootimgram}
-    fi
+    mkdir -m 700  ${srwroot}/cache
+    cp "${rootimg}" ${rootimgram} || rm ${rootimgram}
+    rootimg=${rootimgram}
+fi
 
-    verifyroot "${rootimg}"
+# Set up DM-Verity (sets rootdev=/dev/mapper/...)
+rootdev=/dev/loop0
+modprobe -b loop
+losetup -r ${rootdev} "${rootimg}"
+verifyroot ${rootdev}
 
-    # CONFIG_FEATURE_DEVFS must be disabled for BusyBox
-    good_msg 'Mounting SquashFS filesystem'
-    modprobe -b squashfs
-    mount -r -t squashfs -o loop,${fs_flags_squashfs} "${rootimg}" ${slive}
-    test_success 'mount compressed filesystem'
+# CONFIG_FEATURE_DEVFS must be disabled for BusyBox
+good_msg 'Mounting SquashFS filesystem'
+modprobe -b squashfs
+mount -r -t squashfs -o ${fs_flags_squashfs} ${rootdev} ${slive}
+test_success 'mount compressed filesystem'
 
-    # Move boot mountpoint under live
-    mount -o move ${sboot} ${mboot}
-else
-    # Treat boot mountpoint as live mountpoint
-    verifyroot ${mediadev}
-    mount -o move ${sboot} ${slive}
-fi
+# Move boot mountpoint under live
+mount -o move ${sboot} ${mboot}
 
 # Bind extra live mountpoint under live
-mount -r -o bind ${slive}   ${mlive}
+mount -r -o bind ${slive} ${mlive}
 
 # Move rwroot mountpoint under live
-mount -o move    ${srwroot} ${mrwroot}
+mount -o move ${srwroot} ${mrwroot}
 
 
 # Run debug shell again if requested

+ 3 - 0
src/root/initrd/initramfs

@@ -22,6 +22,9 @@ dir   stage/rwroot   0755 0 0
 dir   bin            0755 0 0
 file  bin/busybox       /bin/busybox              0755 0 0
 
+# Verity
+file  sbin/veritysetup  /sbin/veritysetup         0755 0 0
+
 # Mount points
 dir   proc           0755 0 0
 dir   sys            0755 0 0

+ 2 - 2
src/root/initrd/modules.fs

@@ -1,6 +1,6 @@
 # Modules to force-load during initramfs stage (see modules.init)
 # (no module autoloading on mount in Busybox)
-force_load_fs="squashfs vfat isofs ext4 hfsplus nls_cp437 nls_iso8859-1 nls_utf8"
+force_load_fs="vfat isofs ext4 hfsplus nls_cp437 nls_iso8859-1 nls_utf8"
 
 
 # Default filesystem mount flags
@@ -24,7 +24,7 @@ fs_flags_auto=noatime,nosuid,nodev
 #   + had_cdroot_flags    0 if param_cdroot_flags was empty, 1 otherwise
 set_cdroot_type() {
     case "${param_cdroot_type:=auto}" in
-        vfat|iso9660|ext[234]|hfsplus|squashfs|auto)
+        vfat|iso9660|ext[234]|hfsplus|auto)
             ;;
         *)
             warn_msg "Unknown cdroot_type, using 'auto'"

+ 5 - 0
src/root/initrd/modules.init

@@ -7,6 +7,11 @@ loop
 virtio_pci
 virtio_blk
 
+# DM-Verity (kernel crypto backend)
+dm_verity
+algif_hash
+sha1_generic
+
 
 
 # === Module packages ===

+ 1 - 10
src/root/mkimage

@@ -14,9 +14,6 @@ rootfspfx=${HOME}/config/rootfs
 cdroot=/mnt/boot/cdroot
 imagepfx=/mnt/boot/liberte-${LVERSION}
 
-sqimage=${cdroot}/liberte/boot/root-x86.sfs
-sqsort=${live}/tmp/transient/pkg/squashfs.sort
-
 
 mibsize() {
     local bytes=`stat -c %s "$1"`
@@ -67,13 +64,7 @@ sinfo "Creating SquashFS image"
 rm -rf ${cdroot}
 mkdir  ${cdroot} ${cdroot}/liberte ${cdroot}/liberte/boot
 
-# NOTE: POSIX ACLs are not supported by SquashFS
-#       Non-PaX user xattrs are not supported by tmpfs
-mksquashfs ${live} ${sqimage}                \
-    -noappend -no-progress -no-exports       \
-    -always-use-fragments -comp xz -Xbcj x86 \
-    -sort ${sqsort} -ef ${rootfspfx}.ignore
-chmod go= ${sqimage}
+${helpdir}/mk-squashfs
 
 
 sinfo "Initializing boot environment"