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

Reply via email to