A dm-verity protected filesystem image is not hashed by U-Boot when it is loaded; its integrity is delegated to the kernel, which validates the filesystem on the fly against the roothash taken from the FIT dm-verity subnode. The roothash is therefore the sole integrity anchor for the filesystem, yet fit_config_add_hash() only adds the image node, its hash subnodes and its cipher subnode to the signed region, leaving the dm-verity subnode (roothash, salt and block parameters) unsigned.
An attacker able to rewrite the boot medium could then replace both the filesystem and the roothash, recompute a matching dm-verity tree and keep the configuration signature valid, defeating verified boot for the root filesystem. Add the dm-verity subnode to the list of nodes covered by the configuration signature, both when signing (tools/image-host.c) and when verifying (boot/image-fit-sig.c), so the roothash and salt are authenticated together with the rest of the configuration. Signed-off-by: Daniel Golle <[email protected]> Reviewed-by: Tom Rini <[email protected]> --- v3: use present tense for the pre-patch code description, per Simon Glass's review; 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. The tools/image-host.c hunk now calls the patch 1 helper instead of duplicating the pattern inline (no functional change). Kept Tom's Reviewed-by: the functional change he reviewed is unchanged; the rest is doc/message wording and a mechanical refactor. v2: no changes boot/image-fit-sig.c | 23 +++++++++++++++++++---- doc/usage/fit/dm-verity.rst | 5 +++++ doc/usage/fit/signature.rst | 2 +- tools/image-host.c | 21 +++++++++++++++++++++ 4 files changed, 46 insertions(+), 5 deletions(-) diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c index 3357ec92116..f7ab036dcb5 100644 --- a/boot/image-fit-sig.c +++ b/boot/image-fit-sig.c @@ -264,8 +264,8 @@ static int fit_config_add_node(const void *fit, int noffset, char **node_inc, /** * fit_config_add_hash() - Add hash nodes for one image to the node list * - * Adds the image path, all its hash-* subnode paths, and its cipher - * subnode path (if present) to the packed buffer. + * Adds the image path, all its hash-* subnode paths, and its cipher and + * dm-verity subnode paths (each if present) to the packed buffer. * * @fit: FIT blob * @image_noffset: Image node offset (e.g. /images/kernel-1) @@ -322,6 +322,21 @@ static int fit_config_add_hash(const void *fit, int image_noffset, return ret; } + /* + * Add this image's dm-verity node if present. Its roothash is the + * only integrity anchor for a dm-verity filesystem image, so it must + * be covered by the configuration signature. + */ + noffset = fdt_subnode_offset(fit, image_noffset, FIT_VERITY_NODENAME); + if (noffset != -FDT_ERR_NOTFOUND) { + if (noffset < 0) + return -EIO; + ret = fit_config_add_node(fit, noffset, node_inc, count, + max_nodes, buf, buf_used, buf_len); + if (ret) + return ret; + } + return 0; } @@ -329,8 +344,8 @@ static int fit_config_add_hash(const void *fit, int image_noffset, * fit_config_get_hash_list() - Build the list of nodes to hash * * Works through every image referenced by the configuration and collects the - * node paths: root + config + all referenced images with their hash and - * cipher subnodes. + * node paths: root + config + all referenced images with their hash, + * cipher and dm-verity subnodes. * * Properties known not to be image references (description, compatible, * default, load-only) are skipped, so any new image type is covered by default. diff --git a/doc/usage/fit/dm-verity.rst b/doc/usage/fit/dm-verity.rst index 800a18fceae..76030c751ae 100644 --- a/doc/usage/fit/dm-verity.rst +++ b/doc/usage/fit/dm-verity.rst @@ -209,6 +209,11 @@ typically be obtained from its output. The ``digest`` and ``salt`` byte arrays correspond to the hex-encoded ``Root hash`` and ``Salt`` printed by ``veritysetup format``. +When the configuration is signed, ``digest`` and ``salt`` are covered by +the configuration signature (see :doc:`signature`), so the roothash +cannot be swapped out for a matching one without invalidating the +signature. + Optional boolean properties (when present, they are collected and appended as dm-verity optional parameters with hyphens converted to underscores): diff --git a/doc/usage/fit/signature.rst b/doc/usage/fit/signature.rst index da08cc75c3a..64bada2f58f 100644 --- a/doc/usage/fit/signature.rst +++ b/doc/usage/fit/signature.rst @@ -359,7 +359,7 @@ however, U-Boot does not read 'hashed-nodes'. Instead it rebuilds the node list from the configuration's own image references (kernel, fdt, ramdisk, etc.), since 'hashed-nodes' is not itself covered by the signature. The rebuilt list always includes the root node, the configuration node, each -referenced image node and its hash/cipher subnodes. +referenced image node and its hash, cipher and dm-verity subnodes. The image is walked in order and each tag processed as follows: diff --git a/tools/image-host.c b/tools/image-host.c index fd2ef99d399..16a5ad6c22d 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -1287,6 +1287,27 @@ static int fit_config_add_hash(const void *fit, int image_noffset, return ret; } + /* + * Add this image's dm-verity node if present. Its roothash is the + * only integrity anchor for a dm-verity filesystem image, so it must + * be covered by the configuration signature. + */ + noffset = fdt_subnode_offset(fit, image_noffset, + FIT_VERITY_NODENAME); + if (noffset != -FDT_ERR_NOTFOUND) { + if (noffset < 0) { + fprintf(stderr, + "Failed to get dm-verity node in configuration '%s/%s' image '%s': %s\n", + conf_name, sig_name, iname, + fdt_strerror(noffset)); + return -EIO; + } + ret = fit_config_add_node(fit, noffset, node_inc, conf_name, + sig_name, iname); + if (ret) + return ret; + } + return 0; } -- 2.55.0
