diff --git a/.gitmodules b/.gitmodules index 51292c526a..6509f87c71 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "openembedded-core"] path = openembedded-core - url = http://git.openembedded.org/openembedded-core + url = https://github.com/pohly/openembedded-core.git branch = master [submodule "bitbake"] path = bitbake @@ -28,3 +28,7 @@ [submodule "meta-intel-realsense"] path = meta-intel-realsense url = https://github.com/IntelRealSense/meta-intel-realsense.git +[submodule "meta-security"] + path = meta-security + url = https://github.com/pohly/meta-security.git + branch = swtpm diff --git a/doc/howtos/image-installer.rst b/doc/howtos/image-installer.rst new file mode 100644 index 0000000000..b2d46456d1 --- /dev/null +++ b/doc/howtos/image-installer.rst @@ -0,0 +1,70 @@ +Building with swupd enabled +=========================== + +* git clone --recursive --branch installer-image https://github.com/pohly/intel-iot-refkit.git +* git clone https://git.yoctoproject.org/git/meta-swupd +* cd intel-iot-refkit +* . refkit-init-build-env +* bitbake-layers add-layer `pwd`/../../meta-swupd +* Add to local.conf: + + require conf/distro/include/refkit-development.inc + + REFKIT_IMAGE_COMMON_EXTRA_FEATURES_append = " swupd" + OS_VERSION = "1000" + SWUPD_VERSION_URL = "" + SWUPD_CONTENT_URL = "" + +* bitbake ovmf refkit-installer-image refkit-image-common swtpm-wrappers +* meta-swupd/scripts/swupd-http-server & +* Edit local.conf to build an incremental update: + + OS_VERSION = "1010" + SWUPD_VERSION_URL = "http://localhost:8000" + SWUPD_CONTENT_URL = "http://localhost:8000" + +* bitbake refkit-image-common # Do not rebuild the refkit-installer-image! + +Installing, rebooting, updating +=============================== + +Precondition: user must be able to run commands as root with sudo. + +* cd intel-iot-refkit +* . refkit-init-build-env +* export PATH=../doc/howtos/image-installer:$PATH +* meta-swupd/scripts/swupd-http-server & +* init-tpm # initializes content of a virtual TPM +* run-swtpm # run software TPM in background as root, creates /dev/vtpm0 (must be repeated after each runqemu run!) +* runqemu-install +* Once booting has finished: + * lsblk # dm-verity is active, shown twice and size is a bit odd (hash partition /dev/vda4 should be smaller) + * mount # rootfs is ro + * image-installer + * select refkit-image-common (swupd enabled!), confirm vdb, yes + * reboot +* run-swtpm +* cp tmp-glibc/deploy/images/intel-corei7-64/my-installed-image-intel-corei7-64.wic tmp-glibc/deploy/images/intel-corei7-64/my-installed-image-intel-corei7-64.wic.1000 # can be copied back to repeat the following steps without starting at the top +* runqemu-internal-disk +* Once booted: + * cat /etc/os-release + * lsblk # LUKS crypt active + * mount # rootfs is rw + * connmanctl services + * connmanctl config ethernet_525400123402_cable --ipv4 manual 192.168.7.2 # must match tap0 on host + * swupd verify --url http://192.168.7.1:8000 + * cryptsetup # command not available + * swupd update --url http://192.168.7.1:8000 # fast, incremental update + * cryptsetup status rootfs + * cat /etc/os-release + +Troubleshooting +=============== + +bitbake do_fetch_swupd_inputs fails: SWUPD_VERSION_URL and +SWUPD_CONTENT_URL must be empty for the first build, and non-empty in the +second build. meta-swupd/scripts/swupd-http-server must be running +during the second build. + +qemu can't open /dev/vtpm0: run-swtpm. Must be done after each runqemu invocation +because swtpm shuts down after use. diff --git a/doc/howtos/image-installer/init-tpm b/doc/howtos/image-installer/init-tpm new file mode 100755 index 0000000000..d0d7a3eb59 --- /dev/null +++ b/doc/howtos/image-installer/init-tpm @@ -0,0 +1,6 @@ +#!/bin/sh -ex + +IMAGE_DIR=tmp-glibc/deploy/images/intel-corei7-64 +rm -rf $IMAGE_DIR/my-tpm +mkdir $IMAGE_DIR/my-tpm +tmp-glibc/work/*/swtpm-wrappers/1.0-r0/swtpm_setup_oe.sh --tpm-state $IMAGE_DIR/my-tpm --createek diff --git a/doc/howtos/image-installer/run-swtpm b/doc/howtos/image-installer/run-swtpm new file mode 100755 index 0000000000..cba24884bf --- /dev/null +++ b/doc/howtos/image-installer/run-swtpm @@ -0,0 +1,10 @@ +#!/bin/sh -ex + +IMAGE_DIR=tmp-glibc/deploy/images/intel-corei7-64 +# LOGFILE=tmp-glibc/log/swtpm_cuse.log +# rm -f $LOGFILE +# touch $LOGFILE +# Beware, need absolute paths! +# --log file=$(realpath $LOGFILE),level=20 +sudo tmp-glibc/work/*/swtpm-wrappers/1.0-r0/swtpm_cuse_oe.sh -n vtpm0 --tpmstate dir=$(realpath $IMAGE_DIR/my-tpm) +sudo chown $(id -u) /dev/vtpm0 diff --git a/doc/howtos/image-installer/runqemu-install b/doc/howtos/image-installer/runqemu-install new file mode 100755 index 0000000000..24267c3e53 --- /dev/null +++ b/doc/howtos/image-installer/runqemu-install @@ -0,0 +1,6 @@ +#!/bin/sh -ex + +IMAGE_DIR=tmp-glibc/deploy/images/intel-corei7-64 +truncate -s 4G $IMAGE_DIR/my-installed-image-intel-corei7-64.wic +cp $IMAGE_DIR/refkit-installer-image-intel-corei7-64.qemuboot.conf $IMAGE_DIR/my-installed-image-intel-corei7-64.qemuboot.conf +runqemu serial nographic refkit-installer-image wic intel-corei7-64 "qemuparams=-drive if=virtio,file=$IMAGE_DIR/my-installed-image-intel-corei7-64.wic,format=raw -tpmdev cuse-tpm,id=tpm0,path=/dev/vtpm0 -device tpm-tis,tpmdev=tpm0" ovmf diff --git a/doc/howtos/image-installer/runqemu-internal-disk b/doc/howtos/image-installer/runqemu-internal-disk new file mode 100755 index 0000000000..81d4b9efb7 --- /dev/null +++ b/doc/howtos/image-installer/runqemu-internal-disk @@ -0,0 +1,4 @@ +#!/bin/sh -ex + +IMAGE_DIR=tmp-glibc/deploy/images/intel-corei7-64 +runqemu serial nographic my-installed-image wic intel-corei7-64 "qemuparams=-tpmdev cuse-tpm,id=tpm0,path=/dev/vtpm0 -device tpm-tis,tpmdev=tpm0" ovmf diff --git a/meta-openembedded b/meta-openembedded index 5ecbf9bab4..044e518954 160000 --- a/meta-openembedded +++ b/meta-openembedded @@ -1 +1 @@ -Subproject commit 5ecbf9bab404af0de7d0f058d1620f40dae0d2d8 +Subproject commit 044e5189549de11b2a94efd29a6009a76162b8f1 diff --git a/meta-refkit/classes/image-dsk.bbclass b/meta-refkit/classes/image-dsk.bbclass index f3b7b8397f..d1366a5769 100644 --- a/meta-refkit/classes/image-dsk.bbclass +++ b/meta-refkit/classes/image-dsk.bbclass @@ -75,7 +75,7 @@ DSK_IMAGE_LAYOUT ??= ' \ "partition_01_primary_uefi_boot": { \ "name": "primary_uefi", \ "uuid": 0, \ - "size_mb": 15, \ + "size_mb": ${REFKIT_VFAT_MB}, \ "source": "${IMAGE_ROOTFS}/boot/", \ "filesystem": "vfat", \ "type": "${PARTITION_TYPE_EFI}" \ @@ -83,7 +83,7 @@ DSK_IMAGE_LAYOUT ??= ' \ "partition_02_secondary_uefi_boot": { \ "name": "secondary_uefi", \ "uuid": 0, \ - "size_mb": 15, \ + "size_mb": ${REFKIT_VFAT_MB}, \ "source": "${IMAGE_ROOTFS}/boot/", \ "filesystem": "vfat", \ "type": "${PARTITION_TYPE_EFI_BACKUP}" \ diff --git a/meta-refkit/classes/image-installer.bbclass b/meta-refkit/classes/image-installer.bbclass new file mode 100644 index 0000000000..c7cd85ed50 --- /dev/null +++ b/meta-refkit/classes/image-installer.bbclass @@ -0,0 +1,357 @@ +# This class can be inherited by an arbitrary image recipe to install +# the "image-installer" command plus one or more image files into the +# image. +# +# Basically that turns the image using image-installer.bbclass into an +# "installer image" which can be booted from a removal media to +# install those other images permanently onto internal storage of a +# device. +# +# The "image-installer" is a shell script that gets assembled from +# several fragments provided by image-installer.bbclass, by the distro +# or by the image recipe. The expected usage is that distros using +# image-installer.bbclass will heavily customize the final script. +# +# Because the shell code is embedded in the recipe, regular bitbake +# variables can be injected easily and the shell syntax is checked +# already by the bitbake parser. The downside is that we are limited +# to shell code that the bitbake parser understands (no bash +# extensions, for example) and error reports about the shell syntax +# are a bit hard to read. + +# INSTALLER_SOURCE_IMAGES contains one or more entries of the format +# : which then get copied into +# the rootfs of this image under INSTALLER_IMAGE_DATADIR for use by +# the installer script. +# +# The actual installer code and the input format are tightly coupled, +# so installer code below is just a fake stub which needs to be replaced +# with something that supports both the source images and the target +# hardware. +INSTALLER_SOURCE_IMAGES ??= "" + +INSTALLER_IMAGE_DATADIR = "${libdir}/image-installer" +INSTALLER_BINARY = "${IMAGE_ROOTFS}${sbindir}/image-installer" + +# Sanity check INSTALLER_SOURCE_IMAGES once. +python () { + for entry in d.getVar('INSTALLER_SOURCE_IMAGES').split(): + components = entry.split(':') + if len(components) != 2: + bb.fatal('%s in INSTALLER_SOURCE_IMAGES must have the format :' % entry) +} + +python install_images () { + import os + import shutil + import subprocess + import stat + + deploy_dir_image = d.getVar('DEPLOY_DIR_IMAGE') + install_dir = oe.path.join(d.getVar('IMAGE_ROOTFS'), d.getVar('INSTALLER_IMAGE_DATADIR')) + machine = d.getVar('MACHINE') + for image, suffix in [x.split(':') for x in d.getVar('INSTALLER_SOURCE_IMAGES').split()]: + imagename = '%s-%s.%s' % (image, machine, suffix) + path = os.path.join(deploy_dir_image, imagename) + if not os.path.exists(path): + bb.fatal('Image %s for INSTALLER_SOURCE_IMAGES entry %s:%s not found.' % (path, image, suffix)) + subprocess.check_output(['install', '-d', install_dir]) + # Preserve the symbolic links, because that makes images available + # under a constant name while also keeping the information about the + # exact version of the image which got included. + dest = os.path.join(install_dir, imagename) + if os.path.islink(path): + link_dest = os.path.basename(os.readlink(path)) + os.symlink(link_dest, dest) + os.lchown(dest, 0, 0) + dest = os.path.join(os.path.dirname(dest), link_dest) + path = os.path.realpath(path) + # Hard-linking both saves space during the build *and* + # sparseness of the image file. The fallback doesn't, but shouldn't + # be needed most of the time. + try: + os.link(path, dest) + except IOError as ex: + bb.warn('Hard-linking from %s to %s failed. Falling back to full copy, which looses sparseness: %s' % + (path, dest, str(ex))) + shutil.copy2(path, dest) + # We run under pseudo, so chown/chmod does not really change the attributes + # of the original image file. + os.chown(dest, 0, 0) + mode = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH + os.chmod(dest, mode) +} +do_rootfs[depends] += "${@ ' '.join([x.split(':')[0] + ':do_image_complete' for x in '${INSTALLER_SOURCE_IMAGES}'.split()]) }" +do_rootfs[postfuncs] += " install_images " + + +# The default installer can be used interactively or +# configured to run automatically by setting environment +# variables in advance. +# +# The flow is: +# - determine which image is to be installed +# - determine which disks are suitable as target, automatically +# filtering out the boot disk and everything that is read-only +# or completely unusable (removable media) +# - determine which target to install to +# - execute installation +# +# See below for the commands used by each step. +# +# The default installer code is free of bashisms and passed +# checks with checkbashisms.pl and checkshell. The "local" +# keyword (although supported by bash and dash) is avoided +# in favor of wrapping function bodies with local variables +# in a subshell. +INSTALLER_DEFAULT () { +#!/bin/sh -e + +${INSTALLER_LOGGING} +${INSTALLER_UTILS} +${INSTALLER_PICK_INPUT} +${INSTALLER_FIND_OUTPUT} +${INSTALLER_PICK_OUTPUT} +${INSTALLER_INSTALL} + +pick_input +find_output +pick_output +install_image +info "Installed $CHOSEN_INPUT on $CHOSEN_OUTPUT successfully." +} + +INSTALLER ??= "${INSTALLER_DEFAULT}" + +# Ensure that whatever runtime tools are needed by the installer +# script are actually in the image. +INSTALLER_RDEPENDS ??= " \ + util-linux \ +" +IMAGE_INSTALL_append = " ${INSTALLER_RDEPENDS}" + +# Installs whatever is contained in ${INSTALLER} as ${INSTALLER_BINARY}. +python install_installer () { + import stat + + destfile = d.getVar('INSTALLER_BINARY') + bb.utils.mkdirhier(os.path.dirname(destfile)) + with open(destfile, 'w') as f: + f.write(d.getVar('INSTALLER')) + os.chown(destfile, 0, 0) + mode = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH | \ + stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + os.chmod(destfile, mode) +} +do_rootfs[postfuncs] += " install_installer " + +# Utility code for reporting to the user of the script. +INSTALLER_LOGGING[shellcheck] = "sh" +INSTALLER_LOGGING () { + fatal () { + echo >&2 "ERROR: $*" + exit 1 + } + + error () { + echo >&2 "ERROR: $*" + } + + info () { + echo >/dev/tty "$*" + } +} + +# Cleanup handling, common code for selecting among different input and output +# options interactively, etc. +INSTALLER_UTILS[shellcheck] = "sh" +INSTALLER_UTILS () { + # Skip user interaction. Can be set also in the environment before + # calling the script. + FORCE=${FORCE:-no} + + # log and run a command + execute () { + info "Running: $*" + "$@" + } + + # cleanup commands which are to be run when exiting + CLEANUP_CMDS= + add_cleanup () { + CLEANUP_CMDS="$*; $CLEANUP_CMDS" + } + remove_cleanup () { + CLEANUP_CMDS=$(echo "$CLEANUP_CMDS" | sed -e "s|$*; ||") + } + cleanup () { + eval "$CLEANUP_CMDS" + } + trap cleanup EXIT + + pick_option () ( + ambiguous_msg="$1"; shift + prompt="$1"; shift + chosen="$1"; shift + num_options=$# + + # If something was set already, just use it. + if [ "$chosen" ]; then + echo "$chosen" + return + fi + + if [ "$FORCE" = "yes" ]; then + if [ "$num_options" -ne 1 ]; then + fatal "$ambiguous_msg, cannot proceed: $*" + fi + echo "$1" + else + while true; do + if [ "$num_options" -eq 0 ]; then + printf >/dev/tty "%s" "$prompt [no defaults]: " + read answer + if [ "$answer" ]; then + echo "$answer" + return + fi + else + printf >/dev/tty "%s" "$prompt [" + i=0 + for option in "$@"; do + if [ $i -eq 0 ]; then + printf >/dev/tty "%s" "(RETURN) = (0) = $option" + else + printf >/dev/tty "%s" ", ($i) = $option" + fi + i=$( expr $i + 1 ) + done + printf >/dev/tty "%s" "]: " + + get_option () ( + number=$1; shift + i=0 + for option in "$@"; do + if [ $i -eq "$number" ]; then + echo "$option" + return 0 + fi + i=$( expr $i + 1 ) + done + error "$answer is not a valid shortcut number, try again." + return 1 + ) + + read answer + if [ ! "$answer" ]; then + get_option 0 "$@" && return + elif echo "$answer" | grep -q -e '^[0-9]*$'; then + # Might be an invalid number, so do not return unconditionally! + get_option "$answer" "$@" && return + else + echo "$answer" + return + fi + fi + done + fi + ) + + confirm_install () ( + if [ "$FORCE" != "yes" ]; then + while true; do + printf "%s" 'Proceed? Type "yes" to confirm and "no" to abort: ' + read answer + case $answer in + yes) break;; + no) info "Aborting as requested."; return 1;; + esac + done + fi + ) +} + +INSTALLER_PICK_INPUT[shellcheck] = "sh" +INSTALLER_PICK_INPUT () { + # We know what's going to be available, therefore we don't need code which looks + # for images. + AVAILABLE_INPUT="${@ ' '.join(['%s-${MACHINE}.%s' % (image, suffix) for image, suffix in [x.split(':') for x in d.getVar('INSTALLER_SOURCE_IMAGES').split()]])}" + CHOSEN_INPUT="${CHOSEN_INPUT:-}" + + pick_input () { + # shellcheck disable=SC2086 + CHOSEN_INPUT=$(pick_option "ambiguous input images" "Pick an image file" "$CHOSEN_INPUT" $AVAILABLE_INPUT) || exit 1 + } +} + + +INSTALLER_FIND_OUTPUT[shellcheck] = "sh" +INSTALLER_FIND_OUTPUT () { + AVAILABLE_OUTPUT="" + + _find_output () ( + # Find block devices and filter out our boot device and read-only devices. + local root_device=$(findmnt / --output SOURCE --noheadings) + # Might have been a symlink. + root_device=$(readlink -f "$root_device") + info "Boot device is $root_device." + local exclude= + if [ "$root_device" ]; then + # lsblk knows about the device topology and orders accordingly + # when the NAME column is enabled. Here we are relying on the + # fact that dependent items appear below the disk they are + # rooted in. + local disk= + # shellcheck disable=SC2034 + exclude=$( lsblk --output NAME,KNAME,TYPE --ascii --noheadings | while read name kname type; do + if [ "$type" = "disk" ]; then + disk=$kname + fi + if [ "/dev/$kname" = "$root_device" ]; then + echo "$disk" + break + fi + done ) + fi + + # ignore: + # boot device, + # CD-ROM, + # devices which aren't readable (for example removable device with no media) + AVAILABLE_OUTPUT=$( lsblk --nodeps --output KNAME,TYPE --noheadings | while read kname type; do + if [ "$kname" != "$exclude" ] && + [ "$type" != "rom" ] && + dd "if=/dev/$kname" of=/dev/null bs=1 count=1 2>/dev/null; then + printf " %s" "$kname" + fi + done ) + info "Found the following additional disk(s):$AVAILABLE_OUTPUT" + echo "$AVAILABLE_OUTPUT" + ) + find_output () { + AVAILABLE_OUTPUT=$(_find_output) + } +} + +INSTALLER_PICK_OUTPUT[shellcheck] = "sh" +INSTALLER_PICK_OUTPUT () { + CHOSEN_OUTPUT=${CHOSEN_OUTPUT:-} + pick_output () { + # shellcheck disable=SC2086 + CHOSEN_OUTPUT=$(pick_option "ambiguous target devices" "Pick a target device" "$CHOSEN_OUTPUT" $AVAILABLE_OUTPUT) || exit 1 + } +} + +# This is a non-functional stub which needs to be replaced. +INSTALLER_EMPTY[shellcheck] = "sh" +INSTALLER_EMPTY () { + install_image () ( + input="${INSTALLER_IMAGE_DATADIR}/$CHOSEN_INPUT" + output="/dev/$CHOSEN_OUTPUT" + info "Installing from $input to $output." + + confirm_install || exit 1 + fatal "installation not implemented" + ) +} +INSTALLER_INSTALL ??= "${INSTALLER_EMPTY}" diff --git a/meta-refkit/classes/refkit-image.bbclass b/meta-refkit/classes/refkit-image.bbclass index 78be54db69..afa2ddd2dd 100644 --- a/meta-refkit/classes/refkit-image.bbclass +++ b/meta-refkit/classes/refkit-image.bbclass @@ -60,6 +60,7 @@ IMAGE_FEATURES[validitems] += " \ ima \ smack \ swupd \ + dm-verity \ ${REFKIT_IMAGE_PKG_FEATURES} \ " @@ -105,8 +106,8 @@ IMAGE_INSTALL_append = "${@ ' efi-combo-trigger' if ${REFKIT_USE_DSK_IMAGES} and # Setting a label explicitly on the directory prevents it # from inheriting other undesired attributes like security.SMACK64TRANSMUTE # from upper folders (see xattr-images.bbclass for details). -DEPENDS_${PN}_append = " \ - ${@ bb.utils.contains('IMAGE_FEATURES', 'swupd', 'xattr-native', '', d)} \ +DEPENDS_append = " \ + ${@ bb.utils.contains('IMAGE_FEATURES', 'swupd', 'attr-native', '', d)} \ " fix_var_lib_swupd () { if ${@bb.utils.contains('IMAGE_FEATURES', 'smack', 'true', 'false', d)} && @@ -117,6 +118,36 @@ fix_var_lib_swupd () { } ROOTFS_POSTPROCESS_COMMAND_append = " fix_var_lib_swupd;" +# When using dm-verity, the rootfs has to be read-only. +# An extra partition gets created by wic which holds the +# hash data for the rootfs partition, including a signed +# root hash. +# +# A suitable initramfs (like refkit-initramfs with the dm-verity +# image feature enabled) then validates the signed root +# hash and activates the rootfs. refkit-initramfs checks the +# "dmverity" boot parameter for that. If not present or +# the refkit-initramfs was built without dm-verity support, +# booting proceeds without integrity protection. +# +# TODO: WKS_FILE_DEPENDS currently unused (see https://bugzilla.yoctoproject.org/show_bug.cgi?id=11017), +# one has to set instead: +# DEPENDS_append_pn-wic-tools = " cryptsetup-native openssl-native" +WKS_FILE_DEPENDS_append = " \ + ${@ bb.utils.contains('IMAGE_FEATURES', 'dm-verity', 'cryptsetup-native openssl-native', '', d)} \ +" +REFKIT_DM_VERITY_PARTUUID = "12345678-9abc-def0-0fed-cba987654322" +REFKIT_DM_VERITY_PARTITION () { +part --source dm-verity --uuid ${REFKIT_DM_VERITY_PARTUUID} --label rootfs +} +REFKIT_EXTRA_PARTITION .= "${@ bb.utils.contains('IMAGE_FEATURES', 'dm-verity', d.getVar('REFKIT_DM_VERITY_PARTITION'), '', d) }" +APPEND_append = "${@ bb.utils.contains('IMAGE_FEATURES', 'dm-verity', ' dmverity=PARTUUID=${REFKIT_DM_VERITY_PARTUUID}', '', d) }" +WICVARS_append = "${@ bb.utils.contains('IMAGE_FEATURES', 'dm-verity', ' \ + REFKIT_DMVERITY_PRIVATE_KEY \ + REFKIT_DMVERITY_PASSWORD \ + ', '', d) } \ +" + # Make progress messages from do_swupd_update visible as normal command # line output, instead of just recording it to the logs. Useful # because that task can run for a long time without any output. @@ -274,7 +305,12 @@ IMAGE_FSTYPES_remove = "live" # Activate "dsk" image type. IMAGE_CLASSES += "${@ 'image-dsk' if ${REFKIT_USE_DSK_IMAGES} else ''}" +# By default, the full image is going to use roughly 4GB, independent +# of the actual roofs size. WKS_FILE = "refkit-directdisk.wks.in" +REFKIT_VFAT_MB ??= "40" +REFKIT_IMAGE_SIZE ??= "--fixed-size 3700M" +REFKIT_EXTRA_PARTITION ??= "" WIC_CREATE_EXTRA_ARGS += " -D" # Inherit after setting variables that get evaluated when importing @@ -345,7 +381,9 @@ ima_evm_sign_rootfs_prepend () { APPEND_append = "${@bb.utils.contains('IMAGE_FEATURES', 'smack', '', ' security=none', d)}" # Use what RMC gives, not the defaults in meta-intel machine configs -APPEND_remove_intel-corei7-64 = "console=ttyS0,115200" +# TODO: only use this once qemu also gets a serial console in RMC. +# See https://bugzilla.yoctoproject.org/show_bug.cgi?id=11061 +# APPEND_remove_intel-corei7-64 = "console=ttyS0,115200" # In addition, when Smack is disabled in the image but enabled in the # distro, we strip all Smack xattrs from the rootfs. Otherwise we still diff --git a/meta-refkit/conf/bblayers.conf.sample b/meta-refkit/conf/bblayers.conf.sample index e4c01a1c98..8b4bf6317e 100644 --- a/meta-refkit/conf/bblayers.conf.sample +++ b/meta-refkit/conf/bblayers.conf.sample @@ -1,6 +1,6 @@ # LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf # changes incompatibly -LCONF_VERSION = "1" +LCONF_VERSION = "2" BBPATH = "${TOPDIR}" BBFILES ?= "" @@ -13,19 +13,20 @@ REFKIT_LAYERS = " \ ##OEROOT##/../meta-oic \ ##OEROOT##/../meta-iot-web \ ##OEROOT##/../meta-iotqa \ + ##OEROOT##/../meta-security \ ##OEROOT##/../meta-security-isafw \ ##OEROOT##/../meta-intel-realsense \ " # REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-efl" -# REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-filesystems" +REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-filesystems" REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-gnome" # REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-gpe" # REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-initramfs" # REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-multimedia" REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-networking" REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-oe" -# REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-perl" +REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-perl" REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-python" # REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-ruby" # REFKIT_LAYERS += "##OEROOT##/../meta-openembedded/meta-systemd" diff --git a/meta-refkit/conf/distro/include/refkit-ci.inc b/meta-refkit/conf/distro/include/refkit-ci.inc index d5652f7eed..646b210ee0 100644 --- a/meta-refkit/conf/distro/include/refkit-ci.inc +++ b/meta-refkit/conf/distro/include/refkit-ci.inc @@ -54,8 +54,10 @@ REFKIT_VM_IMAGE_TYPES = "wic.xz wic.zip wic.bmap wic.xz.sha256sum" # which must contain only alphanumeric symbols,'-' and '_'. # Any other symbols would be skipped in parser. # -# Following targets would be used to perform default build task: -REFKIT_CI_BUILD_TARGETS="refkit-image-minimal refkit-image-common refkit-image-computervision refkit-image-gateway" +# Following targets would be used to perform default build task. +# When adding new profile images, add them to refkit-installer-image.bb +# and they will get pulled into the build indirectly. +REFKIT_CI_BUILD_TARGETS="refkit-image-minimal refkit-installer-image" # Following targets would be executed with do_populate_sdk task REFKIT_CI_SDK_TARGETS="" # Following targets would be executed with do_populate_sdk_ext task. diff --git a/meta-refkit/conf/distro/include/refkit-development.inc b/meta-refkit/conf/distro/include/refkit-development.inc index f65cf5cbb3..acefae9be3 100644 --- a/meta-refkit/conf/distro/include/refkit-development.inc +++ b/meta-refkit/conf/distro/include/refkit-development.inc @@ -1,6 +1,13 @@ # Use the pre-generated keys for IMA signing. IMA_EVM_KEY_DIR = "${IMA_EVM_BASE}/data/debug-keys" +# Use the pre-generated OpenSSL keys for dm-verity signing with +# well-known password "refkit" passed directly via the command line. +# Other openssl -passin variants (for example, file:) +# also work. +REFKIT_DMVERITY_PRIVATE_KEY = "${META_REFKIT_BASE}/files/dm-verity/private.pem" +REFKIT_DMVERITY_PASSWORD = "pass:refkit" + # Enable local root access. Always use _append, to # avoid getting this change overwritten by a # REFKIT_IMAGE_EXTRA_FEATURES = "foo" in local.conf. diff --git a/meta-refkit/conf/distro/include/refkit-supported-recipes.txt b/meta-refkit/conf/distro/include/refkit-supported-recipes.txt index 23af53b26e..50233d6ac3 100644 --- a/meta-refkit/conf/distro/include/refkit-supported-recipes.txt +++ b/meta-refkit/conf/distro/include/refkit-supported-recipes.txt @@ -37,6 +37,7 @@ appfw-test-app@iotqa atop@openembedded-layer attr@core autoconf@core +autoconf-archive@openembedded-layer automake@core bad-groups-app@iotqa base-files@core @@ -60,6 +61,7 @@ connman@core coreutils@core cracklib@core cryptodev-linux@core +cryptsetup@openembedded-layer curl@core dash@openembedded-layer db@core @@ -67,6 +69,7 @@ dbus-glib@core dbus-test@core dbus@core diffutils@core +dosfstools@core dri2proto@core e2fsprogs@core efi-combo-trigger@refkit @@ -120,7 +123,10 @@ i2c-quark-board@quark-bsp icu@core iftop@networking-layer ima-evm-utils@integrity +init-ifupdown@core initramfs-framework-ima@integrity +initramfs-framework-refkit-dm-verity@refkit +initramfs-framework-refkit-luks@refkit initramfs-framework@core initscripts@core intel-microcode@intel @@ -140,6 +146,7 @@ keymaps@core keyutils@security-framework kmod@core krb5@openembedded-layer +libaio@core libarchive@core libatomic-ops@core libcap@core @@ -191,6 +198,7 @@ linux-libc-headers@core linux-yocto@core lowpan-tools@networking-layer lttng-ust@core +lvm2@openembedded-layer m4@core make@core mesa@core @@ -198,6 +206,7 @@ mmap-smack-test@security-smack mpfr@core mraa-test@iotqa mraa@refkit +multipath-tools@openembedded-layer ncurses@core netbase@core nettle@core @@ -261,6 +270,7 @@ systemd@core taglib@core tbb@openembedded-layer tcp-smack-test@security-smack +thin-provisioning-tools@openembedded-layer tiff@core tpm-tools@security tremor@core diff --git a/meta-refkit/conf/distro/refkit.conf b/meta-refkit/conf/distro/refkit.conf index 4dc75e1f8e..383a1e5a08 100644 --- a/meta-refkit/conf/distro/refkit.conf +++ b/meta-refkit/conf/distro/refkit.conf @@ -41,6 +41,10 @@ INHERIT += "refkit-update-alternatives" # stateless. PACKAGECONFIG_remove_pn-swupd-client = "stateless" +# temporary workaround: +# fix DEPENDS while patch is pending for OE-core +DEPENDS_append_pn-libarchive-native = " bzip2-replacement-native" + # TODO: # 1. https://github.com/ostroproject/ostro-os-xt/tree/master/meta-ostro-xt/recipes-swupd/swupd-client/swupd-client # has a more recent, shell-based version of the EFI combo updater. @@ -65,10 +69,29 @@ MAINTAINER = "Mikko Ylinen " TARGET_VENDOR = "-refkit" -REFKIT_DEFAULT_DISTRO_FEATURES = "systemd bluez5 pam" +REFKIT_DEFAULT_DISTRO_FEATURES = "systemd bluez5 pam luks tpm1.2 dm-verity" REFKIT_DEFAULT_EXTRA_RDEPENDS ??= "" REFKIT_DEFAULT_EXTRA_RRECOMMENDS ??= "" +# Depening on the distro features we need certain kernel features. The assumption +# here is that all kernels we use support KERNEL_FEATURES *and* have these +# features. +KERNEL_FEATURES_append = " \ + ${@ bb.utils.contains('DISTRO_FEATURES', 'dm-verity', ' features/device-mapper/dm-verity.scc', '', d) } \ + ${@ bb.utils.contains('DISTRO_FEATURES', 'tpm1.2', ' features/tpm/tpm.scc', '', d) } \ +" + +# The upstream recipe does not start tcsd automatically, but we +# want that because the installer image calls the TPM tools +# without starting tcsd first (it shouldn't have to!), and +# without tcsd already running, the tools just fail. A better +# solution would be socket-activation, but tcsd does not support +# that. Does not matter, tcsd is only installed when needed. +SYSTEMD_AUTO_ENABLE_forcevariable_pn-trousers = "enable" + +# A temporary workaround for https://bugzilla.yoctoproject.org/show_bug.cgi?id=11017 +DEPENDS_append_pn-wic-tools = "${@ bb.utils.contains('DISTRO_FEATURES', 'dm-verity', ' cryptsetup-native openssl-native', '', d) }" + # Smack security is a distribution feature which can be enabled or # disabled as needed. To simplify recipes, there is also a smack # override. @@ -303,7 +326,17 @@ PACKAGE_ARCH_pn-rhino = "${TUNE_PKGARCH}" require conf/distro/include/yocto-uninative.inc INHERIT += "uninative" +# temporary workaround: dm-verity.scc already submitted and merged, but not +# all SRCREVs updated +SRCREV_meta_pn-linux-yocto = "7e8ec462b6bc68cb0f3cb0064909e035db1c4038" +SRCREV_meta_pn-linux-intel = "7e8ec462b6bc68cb0f3cb0064909e035db1c4038" + # Disable running fsck at boot. System clock is typically wrong at early boot # stage due to lack of RTC backup battery. This causes unnecessary fixes being # made due to filesystem metadata time stamps being in future. APPEND_append = " fsck.mode=skip" + + +# Blacklisted in meta-openembedded because it fails on arm. +# We can override that for now. +PNBLACKLIST[multipath-tools] = "" diff --git a/meta-refkit/conf/layer.conf b/meta-refkit/conf/layer.conf index 1f092d70a5..00f86b8e91 100644 --- a/meta-refkit/conf/layer.conf +++ b/meta-refkit/conf/layer.conf @@ -39,7 +39,7 @@ REFKIT_LOCALCONF_VERSION = "2" LOCALCONF_VERSION = "${REFKIT_LOCALCONF_VERSION}" # Same for LCONF_VERSION in bblayer.conf.sample. -REFKIT_LAYER_CONF_VERSION = "1" +REFKIT_LAYER_CONF_VERSION = "2" LAYER_CONF_VERSION = "${REFKIT_LAYER_CONF_VERSION}" # The default error messages use shell meta* wildcards to find the @@ -48,3 +48,7 @@ LAYER_CONF_VERSION = "${REFKIT_LAYER_CONF_VERSION}" # user confusion. SANITY_LOCALCONF_SAMPLE = "${META_REFKIT_BASE}/conf/local.conf.sample" SANITY_BBLAYERCONF_SAMPLE = "${META_REFKIT_BASE}/conf/bblayers.conf.sample" + +# acpica from meta-oe no longer works, we need to use the one from +# OE-core. Temporary workaround until meta-oe/acpica is gone. +BBMASK .= "meta-openembedded/meta-oe/recipes-extended/acpica/" diff --git a/meta-refkit/files/dm-verity/private.pem b/meta-refkit/files/dm-verity/private.pem new file mode 100644 index 0000000000..eded30c17a --- /dev/null +++ b/meta-refkit/files/dm-verity/private.pem @@ -0,0 +1,54 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,3934235B9433071CB80CC7F1B0A9F7ED + +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 +-----END RSA PRIVATE KEY----- diff --git a/meta-refkit/recipes-image/images/initramfs-framework-refkit-dm-verity.bb b/meta-refkit/recipes-image/images/initramfs-framework-refkit-dm-verity.bb new file mode 100644 index 0000000000..76fe2c9b69 --- /dev/null +++ b/meta-refkit/recipes-image/images/initramfs-framework-refkit-dm-verity.bb @@ -0,0 +1,109 @@ +# This recipe creates a module for the initramfs-framework in OE-core +# which opens the partition identified via the "root" +# kernel parameter using the dm-verity hashes stored in +# the partition identified via the "dmverity" kernel +# parameter and changes bootparam_root so +# that the following init code uses the read-only, +# integrity protected mapped partition. + +SUMMARY = "dm-verity module for the modular initramfs system" +LICENSE = "MIT" +DEPENDS = "openssl-native" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" + +require refkit-boot-settings.inc + +refkit_dmverity[shellcheck] = "sh" +refkit_dmverity () { + + dmverity_enabled() { + [ "$bootparam_dmverity" ] + } + + dmverity_run () { + C=0 + delay=${bootparam_rootdelay:-1} + timeout=${bootparam_roottimeout:-5} + + if [ "$(echo "$bootparam_root" | cut -c1-5)" = "UUID=" ]; then + root_uuid=$(echo "$bootparam_root" | cut -c6-) + bootparam_root=/dev/disk/by-uuid/$root_uuid + fi + + if [ "$(echo "$bootparam_root" | cut -c1-9)" = "PARTUUID=" ]; then + root_uuid=$(echo "$bootparam_root" | cut -c10-) + bootparam_root=/dev/disk/by-partuuid/$root_uuid + fi + + if [ "$(echo "$bootparam_dmverity" | cut -c1-5)" = "UUID=" ]; then + root_uuid=$(echo "$bootparam_dmverity" | cut -c6-) + bootparam_dmverity=/dev/disk/by-uuid/$root_uuid + fi + + if [ "$(echo "$bootparam_dmverity" | cut -c1-9)" = "PARTUUID=" ]; then + root_uuid=$(echo "$bootparam_dmverity" | cut -c10-) + bootparam_dmverity=/dev/disk/by-partuuid/$root_uuid + fi + + while true; do + seconds=$( expr "$C" '*' "$delay" ) + # shellcheck disable=SC2035 + if [ "$seconds" -gt "$timeout" ]; then + fatal "root partition $bootparam_root and/or dm-verity hash partition $bootparam_dmverity not found." + fi + + if [ -e "$bootparam_root" ] && [ -e "$bootparam_dmverity" ]; then + signature=$(grep -n -m 1 -e "^signature=" "$bootparam_dmverity") + if [ ! "$signature" ]; then + fatal "no signature found in dm-verity hash partition $bootparam_dmverity" + fi + header_lines="$( expr $(echo "$signature" | sed -e 's/:.*//') - 1 )" + header=$(mktemp) + if ! head "-$header_lines" "$bootparam_dmverity" >"$header"; then + fatal "failed to read header from $bootparam_dmverity" + fi + sigfile=$(mktemp) + echo "$signature" | sed -e 's/^[0-9]*:signature=//' | openssl base64 -d >"$sigfile" + result=$(openssl dgst -sha256 -verify /etc/dm-verity-pubkey.pem -signature "$sigfile" "$header") + if [ "$?" != 0 ] || [ "$result" != "Verified OK" ]; then + fatal "dm-verity header in $bootparam_dmverity did not pass OpenSSL signature verification" + fi + + eval $(grep -e ^roothash= -e ^headersize= "$header") + if ! veritysetup create "${REFKIT_DEVICE_MAPPER_ROOTFS_NAME}" "$bootparam_root" "$bootparam_dmverity" "$roothash" --hash-offset "$headersize"; then + fatal "veritysetup of rootfs $bootparam_root using dm-verity hash partition $bootparam_dmverity failed" + fi + bootparam_root="/dev/mapper/${REFKIT_DEVICE_MAPPER_ROOTFS_NAME}" + return + fi + + debug "Sleeping for $delay second(s) to wait root to settle..." + sleep "$delay" + C=$( expr $C + 1 ) + done + } + +} + +python do_install () { + import os + import subprocess + + os.makedirs(os.path.join(d.getVar('D'), 'init.d')) + with open(os.path.join(d.getVar('D'), 'init.d', '80-dmverity'), 'w') as f: + f.write(d.getVar('refkit_dmverity')) + + privkey = d.getVar('REFKIT_DMVERITY_PRIVATE_KEY') + password = d.getVar('REFKIT_DMVERITY_PASSWORD') + pubkey = os.path.join(d.getVar('D'), 'etc', 'dm-verity-pubkey.pem') + os.makedirs(os.path.dirname(pubkey)) + subprocess.check_output(['openssl', 'rsa', '-in', privkey, '-passin', password, '-pubout', '-out', pubkey], + stderr=subprocess.STDOUT) +} + +FILES_${PN} = "/init.d /etc" +RDEPENDS_${PN} += " \ + initramfs-framework-base \ + cryptsetup \ + openssl \ +" diff --git a/meta-refkit/recipes-image/images/initramfs-framework-refkit-luks.bb b/meta-refkit/recipes-image/images/initramfs-framework-refkit-luks.bb new file mode 100644 index 0000000000..14770498d6 --- /dev/null +++ b/meta-refkit/recipes-image/images/initramfs-framework-refkit-luks.bb @@ -0,0 +1,151 @@ +# This recipe creates a module for the initramfs-framework in OE-core +# which opens the partition identified via the root +# kernel parameter as a LUKS container and changes bootparam_root so +# that the following init code uses the decrypted volume. +# +# Currently a proof-of-concept with a fixed password, do not use in +# production! + +SUMMARY = "LUKS module for the modular initramfs system" +LICENSE = "MIT" +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420" +RDEPENDS_${PN} += "initramfs-framework-base" + +require refkit-boot-settings.inc + +refkit_luks[shellcheck] = "sh" +refkit_luks () { + + luks_enabled() { + [ "$bootparam_root" ] + } + + luks_run () { + C=0 + delay=${bootparam_rootdelay:-1} + timeout=${bootparam_roottimeout:-5} + + if [ "$(echo "$bootparam_root" | cut -c1-5)" = "UUID=" ]; then + root_uuid=$(echo "$bootparam_root" | cut -c6-) + bootparam_root=/dev/disk/by-uuid/$root_uuid + fi + + if [ "$(echo "$bootparam_root" | cut -c1-9)" = "PARTUUID=" ]; then + root_uuid=$(echo "$bootparam_root" | cut -c10-) + bootparam_root=/dev/disk/by-partuuid/$root_uuid + fi + + while true; do + seconds=$( expr "$C" '*' "$delay" ) + # shellcheck disable=SC2035 + if [ "$seconds" -gt "$timeout" ]; then + fatal "LUKS root partition $bootparam_root not found." + fi + + # The same refkit-initramfs is used for live boot from USB stick + # and for locked-down booting from internal disk, therefore it + # has to support booting with and without encryption. + # + # TODO: However, when booting from an internal disk, it must + # enforce encryption, otherwise an attacker could downgrade + # the installation from encrypted LUKS to something unencrypted + # under his control. The data would be gone, but integrity + # protection would have failed. + # + # TODO: use two different initramfs variants and set up devices + # so that they only boot less secure installer images until + # installed, then reject them in the future. + if [ -e "$bootparam_root" ]; then + cryptsetup isLuks "$bootparam_root" 2>/dev/null + case $? in + 0) # LUKS volumne found + keyfile=$(mktemp) + keyfile_offset= + tcsd_pid= + luks_cleanup () { + dd if=/dev/zero of="$keyfile" count=1 bs="$(stat -c '%s' "$keyfile")" + rm "$keyfile" + if [ "$tcsd_pid" ]; then + kill "$tcsd_pid" + fi + ifdown lo + } + if ${@ bb.utils.contains('DISTRO_FEATURES', 'tpm1.2', 'true', 'false', d) }; then + # Bring up IPv4 (needed by tcsd and tpm-tools) and tcsd itself. + ifup lo + tcsd -f & + tcsd_pid=$! + while true; do + if ! kill -0 "$tcsd_pid"; then + luks_cleanup + fatal "tcsd terminated unexpectedly" + fi + # Once tcsd has sockets open, we can talk to it. + # shellcheck disable=SC2010 + if ls -l "/proc/$tcsd_pid/fd" | grep -q -w "socket"; then + break + fi + sleep 1 + done + + if ! tpm_nvread -i "${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" -f "$keyfile" -z; then + luks_cleanup + fatal "Error reading NVRAM area with index ${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" + fi + keyfile_offset="${REFKIT_DISK_ENCRYPTION_NVRAM_ID_LEN}" + od "$keyfile" + if [ "$(head -c "$keyfile_offset" "$keyfile")" != "${REFKIT_DISK_ENCRYPTION_NVRAM_ID}" ]; then + luks_cleanup + fatal "Unexpected content in NVRAM area" + fi + # Lock access until next reboot. + if ! tpm_nvread -i "${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" -s 0 -z; then + luks_cleanup + fatal "Error locking NVRAM area with index ${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" + fi + fi + if [ ! -s "$keyfile" ]; then + printf "%s" "${REFKIT_DISK_ENCRYPTION_PASSWORD}" >"$keyfile" + keyfile_offset=0 + fi + if cryptsetup open --type luks "$bootparam_root" "${REFKIT_DEVICE_MAPPER_ROOTFS_NAME}" --key-file "$keyfile" --keyfile-offset "$keyfile_offset"; then + bootparam_root="/dev/mapper/${REFKIT_DEVICE_MAPPER_ROOTFS_NAME}" + luks_cleanup + return + fi + luks_cleanup + ;; + 1) # not a LUKS volume + return + ;; + *) # something else + debug "Error accessing $bootparam_root via cryptsetup" + ;; + esac + fi + + debug "Sleeping for $delay second(s) to wait root to settle..." + sleep "$delay" + C=$( expr $C + 1 ) + done + } + +} + +python do_install () { + import os + + os.makedirs(os.path.join(d.getVar('D'), 'init.d')) + with open(os.path.join(d.getVar('D'), 'init.d', '80-luks'), 'w') as f: + f.write(d.getVar('refkit_luks')) +} + +# netbase is needed because it enables IPv6, and tpm-tools happens +# to communicate with trousers via IPv6. Probably could be reconfigured +# to use only IPv4. + +FILES_${PN} = "/init.d" +RDEPENDS_${PN} = " \ + cryptsetup \ + ${@ bb.utils.contains('DISTRO_FEATURES', 'tpm1.2', 'trousers tpm-tools strace netbase init-ifupdown', '', d) } \ +" diff --git a/meta-refkit/recipes-image/images/refkit-boot-settings.inc b/meta-refkit/recipes-image/images/refkit-boot-settings.inc new file mode 100644 index 0000000000..1ec45fc4ad --- /dev/null +++ b/meta-refkit/recipes-image/images/refkit-boot-settings.inc @@ -0,0 +1,19 @@ +# refkit-initramfs and refkit-installer-image cooperate to implement +# whole-disk encryption. + +# Determines what name is used under /dev/mapper/ for the rootfs. +REFKIT_DEVICE_MAPPER_ROOTFS_NAME ??= "rootfs" + +# Insecure, well-known password as fallback when there is no TPM. +# TODO: ensure that this feature can't be used in production. +REFKIT_DISK_ENCRYPTION_PASSWORD ??= "refkit" + +# Index of the TPM NVRAM area used for random the per-machine disk encryption key. +# The area contains a short ID + version, followed by the key. +REFKIT_DISK_ENCRYPTION_NVRAM_INDEX ??= "1" +REFKIT_DISK_ENCRYPTION_NVRAM_ID ??= "REFKIT_0" +REFKIT_DISK_ENCRYPTION_NVRAM_ID_LEN = "${@ len('${REFKIT_DISK_ENCRYPTION_NVRAM_ID}') }" + +# Default size of the random disk encryption key (only used during installation, +# initramfs determines the size based on what is in the NVRAM area). +REFKIT_DISK_ENCRYPTION_KEY_SIZE ??= "32" diff --git a/meta-refkit/recipes-image/images/refkit-initramfs.bb b/meta-refkit/recipes-image/images/refkit-initramfs.bb index 34e43a075c..0a269987b6 100644 --- a/meta-refkit/recipes-image/images/refkit-initramfs.bb +++ b/meta-refkit/recipes-image/images/refkit-initramfs.bb @@ -30,11 +30,21 @@ IMAGE_FEATURES = "" # Instead we have additional image feature(s). IMAGE_FEATURES[validitems] += " \ ima \ + luks \ + dm-verity \ " IMAGE_FEATURES += " \ ${@bb.utils.contains('DISTRO_FEATURES', 'ima', 'ima', '', d)} \ " FEATURE_PACKAGES_ima = "initramfs-framework-ima" +IMAGE_FEATURES += " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'luks', 'luks', '', d)} \ +" +FEATURE_PACKAGES_luks = "initramfs-framework-refkit-luks" +IMAGE_FEATURES += " \ + ${@bb.utils.contains('DISTRO_FEATURES', 'dm-verity', 'dm-verity', '', d)} \ +" +FEATURE_PACKAGES_dm-verity = "initramfs-framework-refkit-dm-verity" IMAGE_LINGUAS = "" diff --git a/meta-refkit/recipes-image/images/refkit-installer-image.bb b/meta-refkit/recipes-image/images/refkit-installer-image.bb new file mode 100644 index 0000000000..aff5cd9559 --- /dev/null +++ b/meta-refkit/recipes-image/images/refkit-installer-image.bb @@ -0,0 +1,255 @@ +SUMMARY = "IoT Reference OS Kit image with embedded installer." +DESCRIPTION = "IoT Reference OS Kit image with embedded image-installer command for copying IoT Reference OS Kit onto internal storage of a device." + +# The supported format for refkit images is wic because that is already getting built; +# installing files from it is a bit harder than using the tar format, but doable. +# Below, we use the kpartx command to get access to the rootfs files. +INSTALLER_SOURCE_IMAGES ?= " \ + refkit-image-common:wic \ + refkit-image-computervision:wic \ + refkit-image-gateway:wic \ +" + +# Allow wic to resize the image as needed by overriding the default fixes size. +REFKIT_IMAGE_SIZE ?= "" + +require refkit-boot-settings.inc + +# The refkit specific part is derived from the Ostro OS XT installer. +REFKIT_INSTALLER_UEFI_COMBO[shellcheck] = "sh" +REFKIT_INSTALLER_UEFI_COMBO () { + populate () { + output=$1 + gdisk_pnum=$2 + uuid=$3 + rootfs=$4 + output_mounted= + output_mountpoint= + output_luks= + LUKS_NAME=installerrootfs + LUKS_PASSWORD="${REFKIT_DISK_ENCRYPTION_PASSWORD}" + # Use something which is guaranteed to not be persistent. + keydir=$(TMPDIR=/dev/shm mktemp -dt keydir.XXXXXX) + keyfile="$keydir/keyfile" + keyfile_offset= + + cleanup_populate () { + [ "$keydir" ] && (dd if=/dev/zero of="$keyfile" count=1 bs="${REFKIT_DISK_ENCRYPTION_KEY_SIZE}"; rm -rf "$keydir" ) + [ "$output_mounted" ] && execute umount "$output_mountpoint" + [ "$output_mountpoint" ] && rmdir "$output_mountpoint" + [ "$output_luks" ] && execute cryptsetup close "$output_luks" + remove_cleanup cleanup_populate + } + add_cleanup cleanup_populate + + if ! output_mountpoint=$(mktemp -dt output-partition.XXXXXX); then + fatal "could not create mount point" + fi + + # Might be with or without p in the middle (sda1 vs mmcblk0p1). + partition= + for i in $output*$gdisk_pnum; do + if [ -e "$i" ]; then + if [ "$partition" ]; then + fatal "partition #$gdisk_pnum in $output not unique?!" + fi + partition=$i + else + fatal "$output*$gdisk_pnum not found in $output" + fi + done + if [ ! "$partition" ]; then + fatal "could not identify parition #$gdisk_pnum in $output" + fi + + if [ "$uuid" ]; then + if ${@ bb.utils.contains('DISTRO_FEATURES', 'tpm1.2', 'true', 'false', d) }; then + # This uses the well-known (all zero) owner and SRK secrets, + # thus granting any process running on the device access to the + # TPM. + # TODO: lock down access to system processes? + if ! execute tpm_takeownership -y -z; then + fatal "taking ownership of TPM failed - needs to be reset?" + fi + # We store a random key in the TPM NVRAM where it is accessible + # to the initramfs. The initramfs will turn off read-access + # after it has retrieved the key, so nothing else that gets started + # later will have access to the key. + if ! execute tpm_nvdefine -i "${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" -s "$( expr "${REFKIT_DISK_ENCRYPTION_NVRAM_ID_LEN}" + "${REFKIT_DISK_ENCRYPTION_KEY_SIZE}" )" -p 'AUTHREAD|AUTHWRITE|READ_STCLEAR' -y -z; then + fatal "creating NVRAM area failed" + fi + if ! (printf "%s" "${REFKIT_DISK_ENCRYPTION_NVRAM_ID}" && + dd if=/dev/urandom bs="${REFKIT_DISK_ENCRYPTION_KEY_SIZE}" count=1) >"$keyfile"; then + fatal "key creation failed" + fi + keyfile_offset="${REFKIT_DISK_ENCRYPTION_NVRAM_ID_LEN}" + if ! execute tpm_nvwrite -i "${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" -z -f "$keyfile"; then + fatal "storing key in NVRAM failed" + fi + # Lock access until reboot. + if ! execute tpm_nvread -i "${REFKIT_DISK_ENCRYPTION_NVRAM_INDEX}" -z -s 0; then + fatal "locking key in NVRAM failed" + fi + fi + + # Unsafe fallback without TPM: well-known password. + # TODO: detect when this ends up getting used in production. + if [ ! -s "$keyfile" ]; then + printf "%s" "$LUKS_PASSWORD" >"$keyfile" + keyfile_offset=0 + fi + if ${@ bb.utils.contains('DISTRO_FEATURES', 'luks', 'true', 'false', d) }; then + if ! execute cryptsetup luksFormat "$partition" --batch-mode --key-file "$keyfile" --keyfile-offset "$keyfile_offset"; then + fatal "formatting $partition as LUKS contained failed" + fi + if ! execute cryptsetup open --type luks "$partition" "$LUKS_NAME" --key-file "$keyfile" --keyfile-offset "$keyfile_offset"; then + fatal "opening $partition as LUKS container failed" + fi + output_luks=$LUKS_NAME + partition=/dev/mapper/$LUKS_NAME + fi + # Assume that there's only one ext4 partition and it contains root fs (/) + if ! execute mkfs.ext4 -q -v -F -U "$uuid" "$partition"; then + fatal "formatting target rootfs partition $gdisk_pnum failed" + fi + if ! execute mount -t ext4 "$partition" "$output_mountpoint"; then + fatal "mounting target rootfs failed" + else + output_mounted=1 + fi + if ! execute rsync -aAX "$rootfs/" "$output_mountpoint/"; then + fatal "copying rootfs failed" + fi + else + if ! execute mkfs.fat "$partition"; then + fatal "formating vfat partition $gdisk_pnum failed" + fi + if ! execute mount -t vfat "$partition" "$output_mountpoint"; then + fatal "mounting target vfat partition $gdisk_pnum failed" + else + output_mounted=1 + fi + if ! execute cp -r "$rootfs/boot/EFI_internal_storage" "$output_mountpoint/EFI"; then + fatal "copying EFI files failed" + fi + fi + if ! sync; then + fatal "syncing data failed" + fi + cleanup_populate + } + + install_image () { + input="${INSTALLER_IMAGE_DATADIR}/$CHOSEN_INPUT" + output="/dev/$CHOSEN_OUTPUT" + info "Installing from $input to $output." + + confirm_install || return 1 + + input_mountpoint= + input_mounted= + + cleanup_install_image () { + [ "$input_mounted" ] && execute umount "$input_mountpoint" + [ "$input_mountpoint" ] && rmdir "$input_mountpoint" + [ "$input" ] && execute kpartx -d "$input" + remove_cleanup cleanup_install_image + } + add_cleanup cleanup_install_image + + # Assume that there's only one ext4 partition at the end and it + # contains the systems' rootfs. + loopdev=$(execute kpartx -sav "$input" | tail -1 | sed -e 's/^\(add map \)*\([^ ]*\).*/\2/') + if [ ! "$loopdev" ]; then + fatal "kpartx failed for $input" + fi + if ! input_mountpoint=$(mktemp -dt input-rootfs.XXXXXX); then + fatal "could not create mount point" + fi + if ! execute mount "/dev/mapper/$loopdev" "$input_mountpoint"; then + fatal "count not mount rootfs from /dev/mapper/$loopdev" + else + input_mounted=1 + fi + + # Clear all partition data on disk + if ! execute sgdisk -o "$output"; then + fatal "sgdisk $output has failed - damaged disk?" + fi + + # Read partition description from rootfs. + if ! . "$input_mountpoint/boot/emmc-partitions-data"; then + fatal "reading $input_mountpoint/boot/emmc-partitions-data failed" + fi + + # Create partitions. + pnum=0 + gdisk_pnum=1 + while [ "$pnum" -lt "$PART_COUNT" ]; do + eval size="\$PART_${pnum}_SIZE" + eval uuid="\$PART_${pnum}_UUID" + eval type_id="\$PART_${pnum}_TYPE" + eval lname="\$PART_${pnum}_NAME" + eval fs="\$PART_${pnum}_FS" + + if [ "$gdisk_pnum" -eq "$PART_COUNT" ]; then + # Make the last partition take the rest of the space + if ! execute sgdisk -n "$gdisk_pnum:+0:-1s" -c "$gdisk_pnum:$lname" \ + -t "$gdisk_pnum:$type_id" -u "${gdisk_pnum}:${uuid}" -- "$output"; then + fatal "creating rootfs partition failed" + fi + else + if ! execute sgdisk -n "$gdisk_pnum:+0:+${size}M" -c "$gdisk_pnum:$lname" \ + -t "$gdisk_pnum:$type_id" -u "${gdisk_pnum}:${uuid}" -- "$output"; then + fatal "creating vfat partition $gdisk_pnum failed" + fi + fi + + if [ "$gdisk_pnum" -eq 1 ]; then + # Set bootable flag on the first partition + if ! execute sgdisk -A "${gdisk_pnum}:set:2" -- "$output"; then + fatal "making first partition bootable failed" + fi + fi + + if [ "$lname" = "rootfs" ]; then + populate "$output" "$gdisk_pnum" "$uuid" "$input_mountpoint" + else + populate "$output" "$gdisk_pnum" "" "$input_mountpoint" + fi + + pnum=$(expr $pnum + 1) + gdisk_pnum=$(expr $gdisk_pnum + 1) + done + cleanup_install_image + } +} +INSTALLER_INSTALL ?= "${REFKIT_INSTALLER_UEFI_COMBO}" + +INSTALLER_RDEPENDS_append = " \ + dosfstools \ + e2fsprogs-mke2fs \ + gptfdisk \ + kpartx \ + rsync \ + ${@ bb.utils.contains('DISTRO_FEATURES', 'luks', 'cryptsetup', '', d) } \ + ${@ bb.utils.contains('DISTRO_FEATURES', 'tpm1.2', 'trousers tpm-tools', '', d) } \ +" + + +inherit image-installer + +# When dm-verity support is enabled in the distro, the installer image +# by default uses a read-only partition with dm-verity used for integrity +# protection. This has the useful effect that corrupted data on a USB +# stick gets detected instead of silently writing a broken image to +# internal storage. +REFKIT_INSTALLER_IMAGE_EXTRA_FEATURES ?= " \ + ${@ bb.utils.contains('DISTRO_FEATURES', 'dm-verity', 'read-only-rootfs dm-verity', '', d) } \ + ${REFKIT_IMAGE_FEATURES_COMMON} \ +" +REFKIT_INSTALLER_IMAGE_EXTRA_INSTALL ?= "${REFKIT_IMAGE_INSTALL_COMMON}" +REFKIT_IMAGE_EXTRA_FEATURES += "${REFKIT_INSTALLER_IMAGE_EXTRA_FEATURES}" +REFKIT_IMAGE_EXTRA_INSTALL += "${REFKIT_INSTALLER_IMAGE_EXTRA_INSTALL}" + +inherit refkit-image diff --git a/meta-refkit/recipes-swupd/efi-combo-trigger/efi-combo-trigger.bb b/meta-refkit/recipes-swupd/efi-combo-trigger/efi-combo-trigger.bb index a5fc57c694..4389f8ee48 100644 --- a/meta-refkit/recipes-swupd/efi-combo-trigger/efi-combo-trigger.bb +++ b/meta-refkit/recipes-swupd/efi-combo-trigger/efi-combo-trigger.bb @@ -1,6 +1,7 @@ DESCRIPTION = "swupd plugin for updating the kernel+initramfs combo in the EFI system partition" PV = "1.0" LICENSE = "MIT" +DEPENDS = "glib-2.0" SRC_URI = " \ file://efi_combo_updater.c \ diff --git a/meta-refkit/scripts/lib/wic/plugins/source/dm-verity.py b/meta-refkit/scripts/lib/wic/plugins/source/dm-verity.py new file mode 100644 index 0000000000..9167d10f76 --- /dev/null +++ b/meta-refkit/scripts/lib/wic/plugins/source/dm-verity.py @@ -0,0 +1,111 @@ +# Copyright (c) 2017, Intel Corporation. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +# DESCRIPTION +# This source plugin can be used for a partition following sometime after +# the main rootfs in a wic file to generate a partition containing +# dm-verity hash data for the rootfs. +# +# AUTHORS +# Patrick Ohly +# + +import base64 +import glob +import os +import re +import shutil +import tempfile + +from wic import msger +from wic.pluginbase import SourcePlugin +from wic.utils.misc import (exec_cmd, exec_native_cmd, get_bitbake_var) + +class DMVerityPlugin(SourcePlugin): + """ + Creates dm-verity hash data for one rootfs partition, as identified by + the --label parameter. + """ + + name = 'dm-verity' + + @classmethod + def do_prepare_partition(cls, part, source_params, creator, cr_workdir, + oe_builddir, bootimg_dir, kernel_dir, + rootfs_dir, native_sysroot): + """ + Called to do the actual content population for a partition i.e. it + 'prepares' the partition to be incorporated into the image. + In this case, locate the temporary root partition and hash it. + """ + + # We rely on the --label parameter and the naming convention + # in partition.py prepare_rootfs() here to find the already + # prepared rootfs partition image. + pattern = '%s/rootfs_%s.*' % (cr_workdir, part.label) + rootfs = glob.glob(pattern) + if len(rootfs) != 1: + msger.error("%s shell pattern does not match exactly one rootfs image (missing --label parameter?): %s" % (pattern, rootfs)) + else: + rootfs = rootfs[0] + msger.debug("Calculating dm-verity hash for rootfs %s (native %s)." % (rootfs, native_sysroot)) + + hashimg = '%s/dm-verity_%s.img' % (cr_workdir, part.label) + # Reserve some fixed amount of space at the start of the hash image + # for our own data (in particular, the signed root hash). + # The content of that part is: + # roothash=<....> + # + # signature= + header_size = 4096 + ret, out = exec_native_cmd("veritysetup format '%s' '%s' --hash-offset=%d" % + (rootfs, hashimg, header_size), + native_sysroot) + m = re.search(r'^Root hash:\s*(\S+)$', out, re.MULTILINE) + if ret or not m: + msger.error('veritysetup failed: %s' % out) + else: + root_hash = m.group(1) + privkey = get_bitbake_var('REFKIT_DMVERITY_PRIVATE_KEY') + password = get_bitbake_var('REFKIT_DMVERITY_PASSWORD') + tmp = tempfile.mkdtemp(prefix='dm-verity-') + try: + data_filename = os.path.join(tmp, 'data') + header = ('roothash=%s\nheadersize=%d\n' % (root_hash, header_size)).encode('ascii') + with open(data_filename, 'wb') as data: + data.write(header) + # Must use a temporary file, exec_native_cmd() only supports UTF-8 output. + signature = os.path.join(tmp, 'sig') + ret, out = exec_native_cmd("openssl dgst -sha256 -passin '%s' -sign '%s' -out '%s' '%s'" % + (password, privkey, signature, data_filename), + native_sysroot) + if ret: + msger.error('openssl signing failed') + with open(signature, 'rb') as f: + header += b'signature=' + base64.standard_b64encode(f.read()) + b'\n' + if len(header) + 1 >= header_size: + msger.error('reserved space for dm-verity header too small') + with open(hashimg, 'rb+') as hash: + hash.write(header) + finally: + shutil.rmtree(tmp) + + data_bytes = os.stat(rootfs).st_size + hash_bytes = os.stat(hashimg).st_size + msger.debug("dm-verity data partition %d bytes, hash partition %d bytes, ratio %f." % + (data_bytes, hash_bytes, data_bytes / hash_bytes)) + part.size = data_bytes // 1024 + part.source_file = hashimg diff --git a/meta-refkit/wic/refkit-directdisk.wks.in b/meta-refkit/wic/refkit-directdisk.wks.in index a3cc14324a..17604928d1 100644 --- a/meta-refkit/wic/refkit-directdisk.wks.in +++ b/meta-refkit/wic/refkit-directdisk.wks.in @@ -4,6 +4,7 @@ # EFI stub, kernel, kernel cmdline, and the initrd bootloader --ptable gpt -part --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --fixed-size 30M --label primary_uefi --part-type C12A7328-F81F-11D2-BA4B-00A0C93EC93B --align 1024 --use-uuid -part --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --fixed-size 30M --label secondary_uefi --part-type E3C9E316-0B5C-4DB8-817D-F92DF00215AE --align 1024 --use-uuid -part / --source rootfs --fixed-size 3700M --fstype=ext4 --label rootfs --align 1024 --uuid ${REMOVABLE_MEDIA_ROOTFS_PARTUUID_VALUE} +part --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --fixed-size ${REFKIT_VFAT_MB}M --label primary_uefi --part-type C12A7328-F81F-11D2-BA4B-00A0C93EC93B --align 1024 --use-uuid +part --source rootfs --rootfs-dir=${IMAGE_ROOTFS}/boot --fstype=vfat --fixed-size ${REFKIT_VFAT_MB}M --label secondary_uefi --part-type E3C9E316-0B5C-4DB8-817D-F92DF00215AF --align 1024 --use-uuid +part / --source rootfs ${REFKIT_IMAGE_SIZE} --fstype=ext4 --label rootfs --align 1024 --uuid ${REMOVABLE_MEDIA_ROOTFS_PARTUUID_VALUE} +${REFKIT_EXTRA_PARTITION} diff --git a/meta-security b/meta-security new file mode 160000 index 0000000000..28a45876c1 --- /dev/null +++ b/meta-security @@ -0,0 +1 @@ +Subproject commit 28a45876c170ac7913df2b4ad78577bbc0b0790f diff --git a/openembedded-core b/openembedded-core index d1109378d7..59772e0cfe 160000 --- a/openembedded-core +++ b/openembedded-core @@ -1 +1 @@ -Subproject commit d1109378d730c5cf50240c4d1a468e3aef5208ea +Subproject commit 59772e0cfe15561693978e34b883f9a816de4c77