A signed FIT configuration can delegate the integrity of a (potentially
large) root filesystem image to the kernel's dm-verity instead of having
U-Boot hash the whole payload at boot: the FIT carries a "dm-verity"
subnode with the roothash, salt and block parameters, U-Boot passes the
roothash to Linux through the dm-mod.create bootargs, and dm-verity then
validates the filesystem block by block against it.
For that to be safe the roothash has to be trusted, and in a signed
configuration the only thing that establishes trust is the configuration
signature. The roothash was not covered by it. fit_config_add_hash()
collected the image node, its hash subnodes and its cipher subnode into
the signed region, but not the dm-verity subnode, so the roothash, the
sole integrity anchor for the filesystem, was left unsigned.
The result is a verified-boot bypass for the root filesystem: an
attacker who can rewrite the boot medium can replace the filesystem,
recompute a matching dm-verity tree, write the new roothash into the
unsigned dm-verity subnode, and the configuration signature still
verifies. dm-verity then faithfully validates the malicious filesystem
against the attacker's roothash.
This series closes the gap.
v3: address comments by Simon Glass
* factor tools/image-host.c's node-path collection into patch 1 too,
mirroring the boot-side helper, so both the sign side and the verify
side share the same shape and stay easy to compare
* use present tense for the pre-patch code description in patch 2;
document the dm-verity subnode in the rebuilt node list in
doc/usage/fit/signature.rst and note the signature coverage in
doc/usage/fit/dm-verity.rst
* in the unit test, bound-check fdt_find_regions()'s returned count
the same way fit_config_check_sig() does, tamper the digest through
the buffer instead of casting away const, and note in a comment that
the digest check stands in for the whole dm-verity node
* fix test_fit_verity_roothash_signed(), which never actually ran: its
ITS baked the tmpdir prefix into the /incbin/() paths while dtc also
resolves incbin paths relative to the .its file's own directory, so
the path was searched doubled and mkimage always failed
v2: address comments by Tom Rini
* drop the VISIBLE_IF_UT visibility macro; fit_config_get_signed_nodes()
is now simply non-static (previously the function would end up being
inlined, so there *is* a real cost to this)
* document test_fit_verity_sign.py with pydoc docstrings including an
ITS example, and add a page under doc/develop/pytest/ so the module
is rendered in the generated documentation
* collect Reviewed-by tags on patches 1 and 2
Daniel Golle (3):
boot: fit: factor out node-path collection in fit_config_add_hash()
boot: fit: cover the dm-verity roothash with the config signature
test: fit: verify dm-verity roothash is covered by the config
signature
boot/image-fit-sig.c | 108 +++++++----
doc/develop/pytest/test_fit_verity_sign.rst | 10 +
doc/usage/fit/dm-verity.rst | 5 +
doc/usage/fit/signature.rst | 2 +-
include/image.h | 23 +++
test/boot/fit_verity.c | 200 +++++++++++++++++++
test/py/tests/test_fit_verity_sign.py | 202 ++++++++++++++++++++
tools/image-host.c | 94 ++++++---
8 files changed, 578 insertions(+), 66 deletions(-)
create mode 100644 doc/develop/pytest/test_fit_verity_sign.rst
create mode 100644 test/py/tests/test_fit_verity_sign.py
base-commit: 042e45e026c77227319a426940577856ffe62e7b
--
2.55.0