[PATCH v4 2/3] boot: fit: cover the dm-verity roothash with the config signature

Daniel Golle <[email protected]>
Newsgroups gmane.comp.boot-loaders.u-boot
Message-ID <3a20f732b3b251967c18d7c3fa9ec4d781a3dad4.1785276461.git.daniel@makrotopia.org>
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]>
Reviewed-by: Simon Glass <[email protected]>
---
v4: collect Reviewed-by from Simon Glass
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.