1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-22 17:35:35 +03:00
systemd/test/units/testsuite-50.sh
Luca Boccassi ac1f3ad05f verity: re-use already open devices if the hashes match
Opening a verity device is an expensive operation. The kernelspace operations
are mostly sequential with a global lock held regardless of which device
is being opened. In userspace jumps in and out of multiple libraries are
required. When signatures are used, there's the additional cryptographic
checks.

We know when two devices are identical: they have the same root hash.
If libcrypsetup returns EEXIST, double check that the hashes are really
the same, and that either both or none have a signature, and if everything
matches simply remount the already open device. The kernel will do
reference counting for us.

In order to quickly and reliably discover if a device is already open,
change the node naming scheme from '/dev/mapper/major:minor-verity' to
'/dev/mapper/$roothash-verity'.

Unfortunately libdevmapper is not 100% reliable, so in some case it
will say that the device already exists and it is active, but in
reality it is not usable. Fallback to an individually-activated
unique device name in those cases for robustness.
2020-07-21 23:42:03 +01:00

130 lines
6.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
set -ex
set -o pipefail
export SYSTEMD_LOG_LEVEL=debug
cleanup()
{
if [ -z "${image_dir}" ]; then
return
fi
rm -rf "${image_dir}"
}
cd /tmp
image_dir="$(mktemp -d -t -p /tmp tmp.XXXXXX)"
if [ -z "${image_dir}" ] || [ ! -d "${image_dir}" ]; then
echo "mktemp under /tmp failed"
exit 1
fi
trap cleanup EXIT
cp /usr/share/minimal.* "${image_dir}/"
image="${image_dir}/minimal"
roothash="$(cat ${image}.roothash)"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect ${image}.raw | grep -q -F -f /usr/lib/os-release
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "Found read-only 'root' partition of type squashfs with verity"
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect ${image}.raw --root-hash=${roothash} --verity-data=${image}.fooverity | grep -q -F -f /usr/lib/os-release
mv ${image}.fooverity ${image}.verity
mv ${image}.foohash ${image}.roothash
mkdir -p ${image_dir}/mount ${image_dir}/mount2
/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
# Verity volume should be shared (opened only once)
/usr/lib/systemd/systemd-dissect --mount ${image}.raw ${image_dir}/mount2
verity_count=$(ls -1 /dev/mapper/ | grep -c verity)
# In theory we should check that count is exactly one. In practice, libdevmapper
# randomly and unpredictably fails with an unhelpful EINVAL when a device is open
# (and even mounted and in use), so best-effort is the most we can do for now
if [ ${verity_count} -lt 1 ]; then
echo "Verity device ${image}.raw not found in /dev/mapper/"
exit 1
fi
umount ${image_dir}/mount
umount ${image_dir}/mount2
systemd-run -t --property RootImage=${image}.raw /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
mv ${image}.verity ${image}.fooverity
mv ${image}.roothash ${image}.foohash
systemd-run -t --property RootImage=${image}.raw --property RootHash=${image}.foohash --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
systemd-run -t --property RootImage=${image}.raw --property RootHash=${roothash} --property RootVerity=${image}.fooverity /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
mv ${image}.fooverity ${image}.verity
mv ${image}.foohash ${image}.roothash
# Make a GPT disk on the fly, with the squashfs as partition 1 and the verity hash tree as partition 2
machine="$(uname -m)"
if [ "${machine}" = "x86_64" ]; then
root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709
verity_guid=2c7357ed-ebd2-46d9-aec1-23d437ec2bf5
elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then
root_guid=44479540-f297-41b2-9af7-d131d5f0458a
verity_guid=d13c5d3b-b5d1-422a-b29f-9454fdc89d76
elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then
root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae
verity_guid=df3300ce-d69f-4c92-978c-9bfb0f38d820
elif [ "${machine}" = "arm" ]; then
root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3
verity_guid=7386cdf2-203c-47a9-a498-f2ecce45a2d6
elif [ "${machine}" = "ia64" ]; then
root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97
verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571
else
echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me"
exit 1
fi
# du rounds up to block size, which is more helpful for partitioning
root_size="$(du -k ${image}.raw | cut -f1)"
verity_size="$(du -k ${image}.verity | cut -f1)"
# 4MB seems to be the minimum size blkid will accept, below that probing fails
dd if=/dev/zero of=${image}.gpt bs=512 count=$((8192+${root_size}*2+${verity_size}*2))
# sfdisk seems unhappy if the size overflows into the next unit, eg: 1580KiB will be interpreted as 1MiB
# so do some basic rounding up if the minimal image is more than 1 MB
if [ ${root_size} -ge 1024 ]; then
root_size="$((${root_size}/1024 + 1))MiB"
else
root_size="${root_size}KiB"
fi
verity_size="${verity_size}KiB"
uuid="$(head -c 32 ${image}.roothash | cut -c -8)-$(head -c 32 ${image}.roothash | cut -c 9-12)-$(head -c 32 ${image}.roothash | cut -c 13-16)-$(head -c 32 ${image}.roothash | cut -c 17-20)-$(head -c 32 ${image}.roothash | cut -c 21-)"
echo -e "label: gpt\nsize=${root_size}, type=${root_guid}, uuid=${uuid}" | sfdisk ${image}.gpt
uuid="$(tail -c 32 ${image}.roothash | cut -c -8)-$(tail -c 32 ${image}.roothash | cut -c 9-12)-$(tail -c 32 ${image}.roothash | cut -c 13-16)-$(tail -c 32 ${image}.roothash | cut -c 17-20)-$(tail -c 32 ${image}.roothash | cut -c 21-)"
echo -e "size=${verity_size}, type=${verity_guid}, uuid=${uuid}" | sfdisk ${image}.gpt --append
sfdisk --part-label ${image}.gpt 1 "Root Partition"
sfdisk --part-label ${image}.gpt 2 "Verity Partition"
loop="$(losetup --show -P -f ${image}.gpt)"
dd if=${image}.raw of=${loop}p1
dd if=${image}.verity of=${loop}p2
losetup -d ${loop}
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root' partition (UUID $(head -c 32 ${image}.roothash)) of type squashfs for .* with verity on partition #1"
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q "Found read-only 'root-verity' partition (UUID $(tail -c 32 ${image}.roothash)) of type DM_verity_hash for .* on partition #2"
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F "MARKER=1"
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} ${image}.gpt | grep -q -F -f /usr/lib/os-release
/usr/lib/systemd/systemd-dissect --root-hash ${roothash} --mount ${image}.gpt ${image_dir}/mount
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/etc/os-release | grep -q -F -f /usr/lib/os-release
cat ${image_dir}/mount/usr/lib/os-release | grep -q -F "MARKER=1"
umount ${image_dir}/mount
systemd-run -t --property RootImage=${image}.gpt --property RootHash=${roothash} /usr/bin/cat /usr/lib/os-release | grep -q -F "MARKER=1"
echo OK > /testok
exit 0