From d5c945bddb41cb45f0139eef3dae9122f8b11af7 Mon Sep 17 00:00:00 2001 From: Simon de Vlieger Date: Fri, 25 Sep 2026 13:50:46 +0200 Subject: [PATCH] dissect: relate verity partitions When using `systemd.image_filter=usr=250` a `/usr` was selected based on its label but the corresponding verity partitions were *not* selected based on their (potential labels). This leads to mismatched verity partitions and `dm-verity` failure when using `image_filter` without also explicitly passing matching `usr-verity` and `usr-verity-sig`. Signed-off-by: Simon de Vlieger (cherry picked from commit b5326670faac3064bcfa01ae3c1d57565d387bfb) --- src/shared/dissect-image.c | 21 +++++++-- src/shared/dissect-image.h | 1 + src/test/test-image-filter.c | 91 ++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index a392f2a949..10ef86ed59 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -966,16 +966,31 @@ static int compare_arch(Architecture a, Architecture b) { return 0; } -static bool image_filter_test(const ImageFilter *filter, PartitionDesignator d, const char *label) { +bool image_filter_test(const ImageFilter *filter, PartitionDesignator d, const char *label) { assert(d < _PARTITION_DESIGNATOR_MAX); if (d < 0) /* For unspecified designators we have no filter expression */ return true; - if (!filter || !filter->pattern[d]) + if (!filter) return true; - return fnmatch(filter->pattern[d], strempty(label), FNM_NOESCAPE) == 0; + const char *pattern = filter->pattern[d]; + + /* If there's no filter for this specific designator but it's a verity partition, fall back to the + * filter for the corresponding data partition. This ensures that e.g. image_filter=usr=250 also + * constrains which usr verity (and verity sig) partitions are selected. This assumes verity + * partitions carry the same label as their corresponding data partition. */ + if (!pattern) { + PartitionDesignator data = partition_verity_to_data(d); + if (data >= 0) + pattern = filter->pattern[data]; + } + + if (!pattern) + return true; + + return fnmatch(pattern, strempty(label), FNM_NOESCAPE) == 0; } static int dissect_image_from_unpartitioned( diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index 3ff2fdb882..7ba04ebfdd 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -203,6 +203,7 @@ int dissected_image_relinquish(DissectedImage *m); void image_filter_done(ImageFilter *f); ImageFilter *image_filter_free(ImageFilter *f); DEFINE_TRIVIAL_CLEANUP_FUNC(ImageFilter*, image_filter_free); +bool image_filter_test(const ImageFilter *filter, PartitionDesignator d, const char *label); int image_filter_parse(const char *s, ImageFilter **ret); int verity_settings_load(VeritySettings *verity, const char *image, const char *root_hash_path, const char *root_hash_sig_path); diff --git a/src/test/test-image-filter.c b/src/test/test-image-filter.c index d5d157727d..acd103db03 100644 --- a/src/test/test-image-filter.c +++ b/src/test/test-image-filter.c @@ -36,4 +36,95 @@ TEST(image_filter) { ASSERT_ERROR(image_filter_parse("root=knuff:root=knuff", &f), EINVAL); } +#if HAVE_BLKID +TEST(image_filter_test_basic) { + _cleanup_(image_filter_freep) ImageFilter *f = NULL; + + /* NULL filter matches everything */ + ASSERT_TRUE(image_filter_test(NULL, PARTITION_ROOT, "foo")); + ASSERT_TRUE(image_filter_test(NULL, PARTITION_USR, "bar")); + ASSERT_TRUE(image_filter_test(NULL, PARTITION_USR_VERITY, "baz")); + + /* Filter on usr matches the right label */ + ASSERT_OK(image_filter_parse("usr=250", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR, "250")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR, "251")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR, "")); + + /* Unfiltered designators pass through */ + ASSERT_TRUE(image_filter_test(f, PARTITION_ROOT, "anything")); + ASSERT_TRUE(image_filter_test(f, PARTITION_HOME, "anything")); +} + +TEST(image_filter_test_verity_fallback) { + _cleanup_(image_filter_freep) ImageFilter *f = NULL; + + /* Filter on usr= should also constrain usr-verity and usr-verity-sig */ + ASSERT_OK(image_filter_parse("usr=250", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY, "250")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY, "251")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "250")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "251")); + + f = image_filter_free(f); + + /* Filter on root= should also constrain root-verity and root-verity-sig */ + ASSERT_OK(image_filter_parse("root=A", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_ROOT_VERITY, "A")); + ASSERT_FALSE(image_filter_test(f, PARTITION_ROOT_VERITY, "B")); + ASSERT_TRUE(image_filter_test(f, PARTITION_ROOT_VERITY_SIG, "A")); + ASSERT_FALSE(image_filter_test(f, PARTITION_ROOT_VERITY_SIG, "B")); + + /* Unrelated verity-less designators are not affected */ + ASSERT_TRUE(image_filter_test(f, PARTITION_HOME, "anything")); + + f = image_filter_free(f); + + /* Glob patterns should work through the fallback too */ + ASSERT_OK(image_filter_parse("usr=25*", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR, "250")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR, "259")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR, "260")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY, "250")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY, "259")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY, "260")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "250")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "259")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "260")); +} + +TEST(image_filter_test_verity_explicit) { + _cleanup_(image_filter_freep) ImageFilter *f = NULL; + + /* Explicit usr-verity= pattern wins over the usr= fallback */ + ASSERT_OK(image_filter_parse("usr=250:usr-verity=999", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR, "250")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR, "999")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY, "999")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY, "250")); + /* usr-verity-sig still falls back to usr= since no explicit usr-verity-sig= was given */ + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "250")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "999")); + + f = image_filter_free(f); + + /* Explicit usr-verity-sig= pattern wins over the usr= fallback */ + ASSERT_OK(image_filter_parse("usr=250:usr-verity-sig=777", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR, "250")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "777")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY_SIG, "250")); + /* usr-verity still falls back to usr= since no explicit usr-verity= was given */ + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY, "250")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY, "777")); + + f = image_filter_free(f); + + /* Fallback does not apply in reverse: usr-verity= must not constrain usr */ + ASSERT_OK(image_filter_parse("usr-verity=999", &f)); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR_VERITY, "999")); + ASSERT_FALSE(image_filter_test(f, PARTITION_USR_VERITY, "250")); + ASSERT_TRUE(image_filter_test(f, PARTITION_USR, "anything")); +} +#endif + DEFINE_TEST_MAIN(LOG_INFO);