+ if (root_path != NULL) {
+ zfree(ZV_NAMEI, root_path);
+ }
+ return result;
+}
+
+extern bool IOBaseSystemARVRootHashAvailable(void);
+
+
+/*
+ * Mounts new filesystem based on image path, and pivots it to the root.
+ * The image to be mounted is located at image_path.
+ * It will be mounted at mount_path.
+ * The vfs_switch_root operation will be performed.
+ * After the pivot, the outgoing root filesystem (the filesystem at root when
+ * this function begins) will be at outgoing_root_path. If `rooted_dmg` is true,
+ * then ignore then chunklisted or authAPFS checks on this image
+ */
+__private_extern__ int
+imageboot_pivot_image(const char *image_path, imageboot_type_t type, const char *mount_path,
+ const char *outgoing_root_path, const bool rooted_dmg)
+{
+ int error;
+ boolean_t authenticated_dmg_chunklist = false;
+ vnode_t mount_vp = NULLVP;
+ errno_t rootauth;
+
+
+ if (type != IMAGEBOOT_DMG) {
+ panic("not supported");
+ }
+
+ /*
+ * Check that the image file actually exists.
+ * We also need to find the mount it's on, to mark it as backing the
+ * root.
+ */
+ vnode_t imagevp = NULLVP;
+ error = vnode_lookup(image_path, 0, &imagevp, vfs_context_kernel());
+ if (error) {
+ printf("%s: image file not found or couldn't be read: %d\n", __FUNCTION__, error);
+ /*
+ * bail out here to short-circuit out of panic logic below.
+ * Failure to find the pivot-image should not be a fatal condition (ENOENT)
+ * since it may result in natural consequences (ergo, cannot unlock filevault prompt).
+ */
+ return error;
+ }
+
+ /*
+ * load the disk image and obtain its device.
+ * di_root_image's name and the names of its arguments suggest it has
+ * to be mounted at the root, but that's not actually needed.
+ * We just need to obtain the device info.
+ */
+
+ dev_t dev;
+ char devname[DEVMAXNAMESIZE];
+
+ error = di_root_image_ext(image_path, devname, DEVMAXNAMESIZE, &dev, true);
+ if (error) {
+ panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
+ }
+
+ printf("%s: attached disk image %s as %s\n", __FUNCTION__, image_path, devname);
+
+
+#if CONFIG_IMAGEBOOT_CHUNKLIST
+ if ((rooted_dmg == false) && !IOBaseSystemARVRootHashAvailable()) {
+ error = authenticate_root_with_chunklist(image_path, NULL);
+ if (error == 0) {
+ printf("authenticated root-dmg via chunklist...\n");
+ authenticated_dmg_chunklist = true;
+ } else {
+ /* root hash was not available, and image is NOT chunklisted? */
+ printf("failed to chunklist-authenticate root-dmg @ %s\n", image_path);
+ }
+ }
+#endif
+
+ char fulldevname[DEVMAXNAMESIZE + 5]; // "/dev/"
+ strlcpy(fulldevname, "/dev/", sizeof(fulldevname));
+ strlcat(fulldevname, devname, sizeof(fulldevname));
+
+ /*
+ * mount expects another layer of indirection (because it expects to
+ * be getting a user_addr_t of a char *.
+ * Make a pointer-to-pointer on our stack. It won't use this
+ * address after it returns so this should be safe.
+ */
+ char *fulldevnamep = &(fulldevname[0]);
+ char **fulldevnamepp = &fulldevnamep;
+
+#define PIVOTMNT "/System/Volumes/BaseSystem"
+
+
+ /* Attempt to mount as HFS; if it fails, then try as APFS */
+ printf("%s: attempting to mount as hfs...\n", __FUNCTION__);
+ error = kernel_mount("hfs", NULLVP, NULLVP, PIVOTMNT, fulldevnamepp, 0, (MNT_RDONLY | MNT_DONTBROWSE), (KERNEL_MOUNT_NOAUTH | KERNEL_MOUNT_BASESYSTEMROOT), vfs_context_kernel());
+ if (error) {
+ printf("mount failed: %d\n", error);
+ printf("%s: attempting to mount as apfs...\n", __FUNCTION__);
+ error = kernel_mount("apfs", NULLVP, NULLVP, PIVOTMNT, fulldevnamepp, 0, (MNT_RDONLY | MNT_DONTBROWSE), (KERNEL_MOUNT_NOAUTH | KERNEL_MOUNT_BASESYSTEMROOT), vfs_context_kernel());
+ }
+
+ /* If we didn't mount as either HFS or APFS, then bail out */
+ if (error) {
+ /*
+ * Note that for this particular failure case (failure to mount), the disk image
+ * being attached may have failed to quiesce within the alloted time out (20-30 sec).
+ * For example, it may be still probing, or APFS container enumeration may have not
+ * completed. If so, then we may have fallen into this particular error case. However,
+ * failure to complete matching should be an exceptional case as 30 sec. is quite a
+ * long time to wait for matching to complete (which would have occurred in
+ * di_root_image_ext).
+ */
+#if defined(__arm64__) && XNU_TARGET_OS_OSX
+ panic("%s: failed to mount pivot image(%d)!", __FUNCTION__, error);
+#endif
+ printf("%s: failed to mount pivot image(%d) !", __FUNCTION__, error);
+ goto done;
+ }
+
+ /* otherwise, if the mount succeeded, then assert that the DMG is authenticated (either chunklist or authapfs) */
+ error = vnode_lookup(PIVOTMNT, 0, &mount_vp, vfs_context_kernel());
+ if (error) {
+#if defined(__arm64__) && XNU_TARGET_OS_OSX
+ panic("%s: failed to lookup pivot root (%d) !", __FUNCTION__, error);
+#endif
+ printf("%s: failed to lookup pivot root (%d)!", __FUNCTION__, error);
+ goto done;
+ }
+
+ /* the 0x1 implies base system */
+ rootauth = VNOP_IOCTL(mount_vp, FSIOC_KERNEL_ROOTAUTH, (caddr_t)0x1, 0, vfs_context_kernel());
+ if (rootauth) {
+ printf("BS-DMG failed to authenticate intra-FS \n");
+ /*
+ * If we are using a custom rooted DMG, or if we have already authenticated
+ * the DMG via chunklist, then it is permissible to use.
+ */
+ if (rooted_dmg || authenticated_dmg_chunklist) {
+ rootauth = 0;
+ }
+ error = rootauth;
+ }
+ vnode_put(mount_vp);
+ mount_vp = NULLVP;
+
+ if (error) {
+ /*
+ * Failure here exclusively means that the mount failed to authenticate.
+ * This means that the disk image either was not sealed (authapfs), or it was
+ * not hosted on a chunklisted DMG. Both scenarios may be fatal depending
+ * on the platform.
+ */
+#if defined(__arm64__) && XNU_TARGET_OS_OSX
+ panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error);
+#endif
+ printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error);
+ goto done;
+ }