unsigned n_followed;
LIST_HEAD(MountOptions, image_options_const);
char **overlay_layers;
+ VeritySettings verity;
} MountEntry;
typedef struct MountList {
p->source_malloc = mfree(p->source_malloc);
p->options_malloc = mfree(p->options_malloc);
p->overlay_layers = strv_free(p->overlay_layers);
+ verity_settings_done(&p->verity);
}
static void mount_list_done(MountList *ml) {
/* First, prepare a mount for each image, but these won't be visible to the unit, instead
* they will be mounted in our propagate directory, and used as a source for the overlay. */
for (size_t i = 0; i < n_mount_images; i++) {
+ _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
_cleanup_free_ char *mount_point = NULL;
const MountImage *m = mount_images + i;
"No matching entry in .v/ directory %s found.",
m->source);
+ r = verity_settings_load(&verity, result.path, /* root_hash_path= */ NULL, /* root_hash_sig_path= */ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to check verity root hash of %s: %m", result.path);
+
if (asprintf(&mount_point, "%s/unit-extensions/%zu", private_namespace_dir, i) < 0)
return -ENOMEM;
.source_malloc = TAKE_PTR(result.path),
.mode = MOUNT_EXTENSION_IMAGE,
.has_prefix = true,
+ .verity = TAKE_GENERIC(verity, VeritySettings, VERITY_SETTINGS_DEFAULT),
};
}
return 0;
}
+static bool verity_has_later_duplicates(MountList *ml, const MountEntry *needle) {
+
+ assert(ml);
+ assert(needle);
+ assert(needle >= ml->mounts && needle < ml->mounts + ml->n_mounts);
+ assert(needle->mode == MOUNT_EXTENSION_IMAGE);
+
+ if (needle->verity.root_hash_size == 0)
+ return false;
+
+ /* Overlayfs rejects supplying the same directory inode twice as determined by filesystem UUID and
+ * file handle in lowerdir=, even if they are mounted on different paths, as it resolves each mount
+ * to its source filesystem, so drop duplicates, and keep the last one. This only covers non-DDI
+ * verity images. Note that the list is ordered, so we only check for the reminder of the list for
+ * each item, rather than the full list from the beginning, as any earlier duplicates will have
+ * already been pruned. */
+
+ for (const MountEntry *m = needle + 1; m < ml->mounts + ml->n_mounts; m++) {
+ if (m->mode != MOUNT_EXTENSION_IMAGE)
+ continue;
+ if (memcmp_nn(m->verity.root_hash,
+ m->verity.root_hash_size,
+ needle->verity.root_hash,
+ needle->verity.root_hash_size) == 0)
+ return true;
+ }
+
+ return false;
+}
+
static void drop_duplicates(MountList *ml) {
MountEntry *f, *t, *previous;
continue;
}
+ if (f->mode == MOUNT_EXTENSION_IMAGE && verity_has_later_duplicates(ml, f)) {
+ log_debug("Skipping duplicate extension image %s", mount_entry_source(f));
+ mount_entry_done(f);
+ continue;
+ }
+
*t = *f;
previous = t;
t++;
}
static int mount_image(
- const MountEntry *m,
+ MountEntry *m,
const char *root_directory,
const ImagePolicy *image_policy) {
host_os_release_sysext_level,
host_os_release_confext_level,
/* required_sysext_scope= */ NULL,
+ &m->verity,
/* ret_image= */ NULL);
if (r == -ENOENT && m->ignore)
return 0;
const char *required_host_os_release_sysext_level,
const char *required_host_os_release_confext_level,
const char *required_sysext_scope,
+ VeritySettings *verity,
DissectedImage **ret_image) {
_cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL;
_cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL;
- _cleanup_(verity_settings_done) VeritySettings verity = VERITY_SETTINGS_DEFAULT;
+ _cleanup_(verity_settings_done) VeritySettings local_verity = VERITY_SETTINGS_DEFAULT;
DissectImageFlags dissect_image_flags;
bool relax_extension_release_check;
int r;
relax_extension_release_check = mount_options_relax_extension_release_checks(options);
- /* We might get an FD for the image, but we use the original path to look for the dm-verity files */
- r = verity_settings_load(&verity, src, NULL, NULL);
- if (r < 0)
- return log_debug_errno(r, "Failed to load root hash: %m");
+ /* We might get an FD for the image, but we use the original path to look for the dm-verity files.
+ * The caller might also give us a pre-loaded VeritySettings, in which case we just use it. It will
+ * also be extended, as dissected_image_load_verity_sig_partition() is invoked. */
+ if (!verity) {
+ r = verity_settings_load(&local_verity, src, NULL, NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to load root hash: %m");
+
+ verity = &local_verity;
+ }
dissect_image_flags =
- (verity.data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
+ (verity->data_path ? DISSECT_IMAGE_NO_PARTITION_TABLE : 0) |
(relax_extension_release_check ? DISSECT_IMAGE_RELAX_EXTENSION_CHECK : 0) |
DISSECT_IMAGE_ADD_PARTITION_DEVICES |
DISSECT_IMAGE_PIN_PARTITION_DEVICES |
src_fd >= 0 ? FORMAT_PROC_FD_PATH(src_fd) : src,
/* open_flags= */ -1,
/* sector_size= */ UINT32_MAX,
- verity.data_path ? 0 : LO_FLAGS_PARTSCAN,
+ verity->data_path ? 0 : LO_FLAGS_PARTSCAN,
LOCK_SH,
&loop_device);
if (r < 0)
r = dissect_loop_device(
loop_device,
- &verity,
+ verity,
options,
image_policy,
dissect_image_flags,
&dissected_image);
/* No partition table? Might be a single-filesystem image, try again */
- if (!verity.data_path && r == -ENOPKG)
+ if (!verity->data_path && r == -ENOPKG)
r = dissect_loop_device(
loop_device,
- &verity,
+ verity,
options,
image_policy,
dissect_image_flags | DISSECT_IMAGE_NO_PARTITION_TABLE,
if (r < 0)
return log_debug_errno(r, "Failed to dissect image: %m");
- r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, &verity);
+ r = dissected_image_load_verity_sig_partition(dissected_image, loop_device->fd, verity);
if (r < 0)
return r;
r = dissected_image_decrypt(
dissected_image,
NULL,
- &verity,
+ verity,
dissect_image_flags);
if (r < 0)
return log_debug_errno(r, "Failed to decrypt dissected image: %m");
int mount_image_privately_interactively(const char *path, const ImagePolicy *image_policy, DissectImageFlags flags, char **ret_directory, int *ret_dir_fd, LoopDevice **ret_loop_device);
-int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_host_os_release_confext_level, const char *required_sysext_scope, DissectedImage **ret_image);
+int verity_dissect_and_mount(int src_fd, const char *src, const char *dest, const MountOptions *options, const ImagePolicy *image_policy, const char *required_host_os_release_id, const char *required_host_os_release_version_id, const char *required_host_os_release_sysext_level, const char *required_host_os_release_confext_level, const char *required_sysext_scope, VeritySettings *verity, DissectedImage **ret_image);
int dissect_fstype_ok(const char *fstype);
/* required_host_os_release_sysext_level= */ NULL,
/* required_host_os_release_confext_level= */ NULL,
/* required_sysext_scope= */ NULL,
+ /* verity= */ NULL,
/* ret_image= */ NULL);
else
r = mount_follow_verbose(LOG_DEBUG, FORMAT_PROC_FD_PATH(chased_src_fd), mount_tmp, NULL, MS_BIND, NULL);
/* required_host_os_release_sysext_level= */ NULL,
/* required_host_os_release_confext_level= */ NULL,
/* required_sysext_scope= */ NULL,
+ /* verity= */ NULL,
&img);
if (r < 0)
return log_debug_errno(
inst_binary mksquashfs
inst_binary unsquashfs
inst_binary pkcheck
+ inst_binary veritysetup
install_verity_minimal
}
--property RootImage="$MINIMAL_IMAGE.raw" \
"${BIND_LOG_SOCKETS[@]}" \
cat /etc/systemd/system/some_file | grep -q -F "MARKER_CONFEXT_123"
+
+# Check that two identical verity images at different paths do not fail with -ELOOP from OverlayFS
+mkdir -p /tmp/loop
+cp /tmp/app0.raw /tmp/loop/app0.raw
+veritysetup format /tmp/loop/app0.raw /tmp/loop/app0.verity --root-hash-file /tmp/loop/app0.roothash
+cp /tmp/loop/app0.raw /tmp/loop/app0_copy.raw
+cp /tmp/loop/app0.verity /tmp/loop/app0_copy.verity
+cp /tmp/loop/app0.roothash /tmp/loop/app0_copy.roothash
+systemd-run -P \
+ --property ExtensionImages=/tmp/loop/app0.raw \
+ --property ExtensionImages=/tmp/loop/app0_copy.raw \
+ --property RootImage="$MINIMAL_IMAGE.raw" \
+ "${BIND_LOG_SOCKETS[@]}" \
+ cat /opt/script0.sh | grep -q -F "extension-release.app0"
+rm -rf /tmp/loop/
+
# Check that using a symlink to NAME-VERSION.raw works as long as the symlink has the correct name NAME.raw
mkdir -p /tmp/symlink-test/
cp /tmp/app-nodistro.raw /tmp/symlink-test/app-nodistro-v1.raw