]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/imageboot.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
CommitLineData
2d21ac55 1/*
f427ee49 2 * Copyright (c) 2006-2020 Apple Computer, Inc. All rights reserved.
2d21ac55
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/param.h>
30#include <sys/kernel.h>
31#include <sys/proc_internal.h>
32#include <sys/systm.h>
33#include <sys/systm.h>
34#include <sys/mount_internal.h>
f427ee49 35#include <sys/fsctl.h>
2d21ac55
A
36#include <sys/filedesc.h>
37#include <sys/vnode_internal.h>
38#include <sys/imageboot.h>
6d2010ae 39#include <kern/assert.h>
2d21ac55 40
813fb2f6
A
41#include <sys/namei.h>
42#include <sys/fcntl.h>
43#include <sys/vnode.h>
44#include <sys/sysproto.h>
45#include <sys/csr.h>
5ba3f43e 46#include <miscfs/devfs/devfsdefs.h>
813fb2f6
A
47#include <libkern/crypto/sha2.h>
48#include <libkern/crypto/rsa.h>
49#include <libkern/OSKextLibPrivate.h>
f427ee49 50#include <sys/ubc_internal.h>
813fb2f6 51
cb323159
A
52#if CONFIG_IMAGEBOOT_IMG4
53#include <libkern/img4/interface.h>
f427ee49 54#include <img4/firmware.h>
cb323159
A
55#endif
56
813fb2f6
A
57#include <kern/kalloc.h>
58
2d21ac55 59#include <pexpert/pexpert.h>
813fb2f6 60#include <kern/chunklist.h>
2d21ac55
A
61
62extern struct filedesc filedesc0;
63
64extern int (*mountroot)(void);
5ba3f43e 65extern char rootdevice[DEVMAXNAMESIZE];
2d21ac55 66
cb323159
A
67#if CONFIG_LOCKERBOOT
68typedef struct _locker_mount_args {
69 char lmnt_path[PATH_MAX];
70 uint16_t lmnt_preferred_hash;
71} locker_mount_args_t;
72#endif
73
2d21ac55
A
74#define DEBUG_IMAGEBOOT 0
75
76#if DEBUG_IMAGEBOOT
cb323159 77#define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__)
2d21ac55
A
78#else
79#define DBG_TRACE(...) do {} while(0)
80#endif
81
cb323159
A
82#define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
83#define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
f427ee49 84#define kheap_free_safe(h, x, l) do { if ((x)) { kheap_free(h, x, l); (x) = NULL; } } while (0)
cb323159 85
f427ee49 86extern int di_root_image_ext(const char *path, char *devname, size_t devsz, dev_t *dev_p, bool removable);
5ba3f43e
A
87extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p);
88extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p);
89
cb323159
A
90static boolean_t imageboot_setup_new(imageboot_type_t type);
91
f427ee49
A
92void *ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size);
93
94extern lck_rw_t * rootvnode_rw_lock;
2d21ac55
A
95
96#define kIBFilePrefix "file://"
97
6d2010ae
A
98__private_extern__ int
99imageboot_format_is_valid(const char *root_path)
100{
0a7de745
A
101 return strncmp(root_path, kIBFilePrefix,
102 strlen(kIBFilePrefix)) == 0;
6d2010ae
A
103}
104
105static void
0a7de745 106vnode_get_and_drop_always(vnode_t vp)
6d2010ae
A
107{
108 vnode_getalways(vp);
109 vnode_rele(vp);
110 vnode_put(vp);
111}
112
f427ee49
A
113__private_extern__ bool
114imageboot_desired(void)
2d21ac55 115{
f427ee49 116 bool do_imageboot = false;
cb323159 117
f427ee49
A
118 char *root_path = NULL;
119 root_path = zalloc(ZV_NAMEI);
120 /*
121 * Check for first layer DMG rooting.
122 *
123 * Note that here we are principally concerned with whether or not we
124 * SHOULD try to imageboot, not whether or not we are going to be able to.
125 *
126 * If NONE of the boot-args are present, then assume that image-rooting
127 * is not requested.
128 *
129 * [!! Note parens guard the entire logically OR'd set of statements, below. It validates
130 * that NONE of the below-mentioned boot-args is present...!!]
131 */
813fb2f6 132 if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
cb323159
A
133#if CONFIG_IMAGEBOOT_IMG4
134 PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) ||
135#endif
0a7de745
A
136 PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
137 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) ||
138 PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) {
f427ee49
A
139 /* explicitly set to false */
140 do_imageboot = false;
141 } else {
142 /* now sanity check the file-path format */
143 if (imageboot_format_is_valid(root_path)) {
144 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
145 /* root_path looks good and we have one of the aforementioned bootargs */
146 do_imageboot = true;
147 } else {
148 /* explicitly set to false */
149 do_imageboot = false;
150 }
6d2010ae 151 }
813fb2f6 152
f427ee49
A
153 zfree(ZV_NAMEI, root_path);
154 return do_imageboot;
155}
156
157__private_extern__ imageboot_type_t
158imageboot_needed(void)
159{
160 imageboot_type_t result = IMAGEBOOT_NONE;
161 char *root_path = NULL;
162
163 DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__);
164
165 if (!imageboot_desired()) {
6d2010ae
A
166 goto out;
167 }
2d21ac55 168
f427ee49 169 root_path = zalloc(ZV_NAMEI);
cb323159 170 result = IMAGEBOOT_DMG;
6d2010ae
A
171
172 /* Check for second layer */
173 if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
0a7de745 174 PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
6d2010ae
A
175 goto out;
176 }
177
178 /* Sanity-check second layer */
179 if (imageboot_format_is_valid(root_path)) {
180 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
181 } else {
182 panic("%s: Invalid URL scheme for %s\n",
0a7de745 183 __FUNCTION__, root_path);
2d21ac55 184 }
6d2010ae
A
185
186out:
f427ee49
A
187 if (root_path != NULL) {
188 zfree(ZV_NAMEI, root_path);
189 }
0a7de745 190 return result;
2d21ac55
A
191}
192
f427ee49
A
193extern bool IOBaseSystemARVRootHashAvailable(void);
194
195
196/*
197 * Mounts new filesystem based on image path, and pivots it to the root.
198 * The image to be mounted is located at image_path.
199 * It will be mounted at mount_path.
200 * The vfs_switch_root operation will be performed.
201 * After the pivot, the outgoing root filesystem (the filesystem at root when
202 * this function begins) will be at outgoing_root_path. If `rooted_dmg` is true,
203 * then ignore then chunklisted or authAPFS checks on this image
204 */
205__private_extern__ int
206imageboot_pivot_image(const char *image_path, imageboot_type_t type, const char *mount_path,
207 const char *outgoing_root_path, const bool rooted_dmg)
208{
209 int error;
210 boolean_t authenticated_dmg_chunklist = false;
211 vnode_t mount_vp = NULLVP;
212 errno_t rootauth;
213
214
215 if (type != IMAGEBOOT_DMG) {
216 panic("not supported");
217 }
218
219 /*
220 * Check that the image file actually exists.
221 * We also need to find the mount it's on, to mark it as backing the
222 * root.
223 */
224 vnode_t imagevp = NULLVP;
225 error = vnode_lookup(image_path, 0, &imagevp, vfs_context_kernel());
226 if (error) {
227 printf("%s: image file not found or couldn't be read: %d\n", __FUNCTION__, error);
228 /*
229 * bail out here to short-circuit out of panic logic below.
230 * Failure to find the pivot-image should not be a fatal condition (ENOENT)
231 * since it may result in natural consequences (ergo, cannot unlock filevault prompt).
232 */
233 return error;
234 }
235
236 /*
237 * load the disk image and obtain its device.
238 * di_root_image's name and the names of its arguments suggest it has
239 * to be mounted at the root, but that's not actually needed.
240 * We just need to obtain the device info.
241 */
242
243 dev_t dev;
244 char devname[DEVMAXNAMESIZE];
245
246 error = di_root_image_ext(image_path, devname, DEVMAXNAMESIZE, &dev, true);
247 if (error) {
248 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
249 }
250
251 printf("%s: attached disk image %s as %s\n", __FUNCTION__, image_path, devname);
252
253
254#if CONFIG_IMAGEBOOT_CHUNKLIST
255 if ((rooted_dmg == false) && !IOBaseSystemARVRootHashAvailable()) {
256 error = authenticate_root_with_chunklist(image_path, NULL);
257 if (error == 0) {
258 printf("authenticated root-dmg via chunklist...\n");
259 authenticated_dmg_chunklist = true;
260 } else {
261 /* root hash was not available, and image is NOT chunklisted? */
262 printf("failed to chunklist-authenticate root-dmg @ %s\n", image_path);
263 }
264 }
265#endif
266
267 char fulldevname[DEVMAXNAMESIZE + 5]; // "/dev/"
268 strlcpy(fulldevname, "/dev/", sizeof(fulldevname));
269 strlcat(fulldevname, devname, sizeof(fulldevname));
270
271 /*
272 * mount expects another layer of indirection (because it expects to
273 * be getting a user_addr_t of a char *.
274 * Make a pointer-to-pointer on our stack. It won't use this
275 * address after it returns so this should be safe.
276 */
277 char *fulldevnamep = &(fulldevname[0]);
278 char **fulldevnamepp = &fulldevnamep;
279
280#define PIVOTMNT "/System/Volumes/BaseSystem"
281
282
283 /* Attempt to mount as HFS; if it fails, then try as APFS */
284 printf("%s: attempting to mount as hfs...\n", __FUNCTION__);
285 error = kernel_mount("hfs", NULLVP, NULLVP, PIVOTMNT, fulldevnamepp, 0, (MNT_RDONLY | MNT_DONTBROWSE), (KERNEL_MOUNT_NOAUTH | KERNEL_MOUNT_BASESYSTEMROOT), vfs_context_kernel());
286 if (error) {
287 printf("mount failed: %d\n", error);
288 printf("%s: attempting to mount as apfs...\n", __FUNCTION__);
289 error = kernel_mount("apfs", NULLVP, NULLVP, PIVOTMNT, fulldevnamepp, 0, (MNT_RDONLY | MNT_DONTBROWSE), (KERNEL_MOUNT_NOAUTH | KERNEL_MOUNT_BASESYSTEMROOT), vfs_context_kernel());
290 }
291
292 /* If we didn't mount as either HFS or APFS, then bail out */
293 if (error) {
294 /*
295 * Note that for this particular failure case (failure to mount), the disk image
296 * being attached may have failed to quiesce within the alloted time out (20-30 sec).
297 * For example, it may be still probing, or APFS container enumeration may have not
298 * completed. If so, then we may have fallen into this particular error case. However,
299 * failure to complete matching should be an exceptional case as 30 sec. is quite a
300 * long time to wait for matching to complete (which would have occurred in
301 * di_root_image_ext).
302 */
303#if defined(__arm64__) && XNU_TARGET_OS_OSX
304 panic("%s: failed to mount pivot image(%d)!", __FUNCTION__, error);
305#endif
306 printf("%s: failed to mount pivot image(%d) !", __FUNCTION__, error);
307 goto done;
308 }
309
310 /* otherwise, if the mount succeeded, then assert that the DMG is authenticated (either chunklist or authapfs) */
311 error = vnode_lookup(PIVOTMNT, 0, &mount_vp, vfs_context_kernel());
312 if (error) {
313#if defined(__arm64__) && XNU_TARGET_OS_OSX
314 panic("%s: failed to lookup pivot root (%d) !", __FUNCTION__, error);
315#endif
316 printf("%s: failed to lookup pivot root (%d)!", __FUNCTION__, error);
317 goto done;
318 }
319
320 /* the 0x1 implies base system */
321 rootauth = VNOP_IOCTL(mount_vp, FSIOC_KERNEL_ROOTAUTH, (caddr_t)0x1, 0, vfs_context_kernel());
322 if (rootauth) {
323 printf("BS-DMG failed to authenticate intra-FS \n");
324 /*
325 * If we are using a custom rooted DMG, or if we have already authenticated
326 * the DMG via chunklist, then it is permissible to use.
327 */
328 if (rooted_dmg || authenticated_dmg_chunklist) {
329 rootauth = 0;
330 }
331 error = rootauth;
332 }
333 vnode_put(mount_vp);
334 mount_vp = NULLVP;
335
336 if (error) {
337 /*
338 * Failure here exclusively means that the mount failed to authenticate.
339 * This means that the disk image either was not sealed (authapfs), or it was
340 * not hosted on a chunklisted DMG. Both scenarios may be fatal depending
341 * on the platform.
342 */
343#if defined(__arm64__) && XNU_TARGET_OS_OSX
344 panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error);
345#endif
346 printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error);
347 goto done;
348 }
349
350 if (rootvnode) {
351 mount_t root_mp = vnode_mount(rootvnode);
352 if (root_mp && (root_mp->mnt_kern_flag & MNTK_SSD)) {
353 rootvp_is_ssd = true;
354 }
355 }
356 /*
357 * pivot the incoming and outgoing filesystems
358 */
359 error = vfs_switch_root(mount_path, outgoing_root_path, 0);
360 if (error) {
361 panic("%s: vfs_switch_root failed: %d\n", __FUNCTION__, error);
362 }
363
364 /*
365 * Mark the filesystem containing the image as backing root, so it
366 * won't be unmountable.
367 *
368 * vfs_switch_root() clears this flag, so we have to set it after
369 * the pivot call.
370 * If the system later pivots out of the image, vfs_switch_root
371 * will clear it again, so the backing filesystem can be unmounted.
372 */
373 mount_t imagemp = imagevp->v_mount;
374 lck_rw_lock_exclusive(&imagemp->mnt_rwlock);
375 imagemp->mnt_kern_flag |= MNTK_BACKS_ROOT;
376 lck_rw_done(&imagemp->mnt_rwlock);
377
378 error = 0;
379
380 /*
381 * Note that we do NOT change kern.bootuuid here -
382 * imageboot_mount_image() does, but imageboot_pivot_image() doesn't.
383 * imageboot_mount_image() is used when the root volume uuid was
384 * "always supposed to be" the one inside the dmg. imageboot_pivot_
385 * image() is used when the true root volume just needs to be
386 * obscured for a moment by the dmg.
387 */
388
389done:
390 if (imagevp != NULLVP) {
391 vnode_put(imagevp);
392 }
393 return error;
394}
395
396/* kern_sysctl.c */
397extern uuid_string_t fake_bootuuid;
398
399static void
400set_fake_bootuuid(mount_t mp)
401{
402 struct vfs_attr va;
403 VFSATTR_INIT(&va);
404 VFSATTR_WANTED(&va, f_uuid);
405
406 if (vfs_getattr(mp, &va, vfs_context_current()) != 0) {
407 return;
408 }
409
410 if (!VFSATTR_IS_SUPPORTED(&va, f_uuid)) {
411 return;
412 }
413
414 uuid_unparse(va.f_uuid, fake_bootuuid);
415}
2d21ac55
A
416
417/*
6d2010ae
A
418 * Swaps in new root filesystem based on image path.
419 * Current root filesystem is removed from mount list and
0a7de745
A
420 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
421 * "rootvnode" is reset. Root vnode of currentroot filesystem
6d2010ae 422 * is returned with usecount (no iocount).
f427ee49
A
423 * kern.bootuuid is arranged to return the UUID of the mounted image. (If
424 * we did nothing here, it would be the UUID of the image source volume.)
2d21ac55 425 */
6d2010ae 426__private_extern__ int
cb323159 427imageboot_mount_image(const char *root_path, int height, imageboot_type_t type)
2d21ac55 428{
0a7de745
A
429 dev_t dev;
430 int error;
cb323159
A
431 /*
432 * Need to stash this here since we may do a kernel_mount() on /, which will
433 * automatically update the rootvnode global. Note that vfs_mountroot() does
434 * not update that global, which is a bit weird.
435 */
436 vnode_t old_rootvnode = rootvnode;
0a7de745
A
437 vnode_t newdp;
438 mount_t new_rootfs;
cb323159 439 boolean_t update_rootvnode = FALSE;
2d21ac55 440
cb323159
A
441 if (type == IMAGEBOOT_DMG) {
442 error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev);
443 if (error) {
444 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
445 }
446
447 rootdev = dev;
448 mountroot = NULL;
449 printf("%s: root device 0x%x\n", __FUNCTION__, rootdev);
450 error = vfs_mountroot();
451 if (error != 0) {
452 panic("vfs_mountroot() failed.\n");
453 }
454
455 update_rootvnode = TRUE;
2d21ac55 456 }
cb323159
A
457#if CONFIG_LOCKERBOOT
458 else if (type == IMAGEBOOT_LOCKER) {
f427ee49
A
459 locker_mount_args_t *mntargs = kheap_alloc(KHEAP_TEMP,
460 sizeof(*mntargs), Z_WAITOK);
cb323159
A
461 if (!mntargs) {
462 panic("could not alloc mount args");
463 }
2d21ac55 464
cb323159
A
465 strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path));
466 mntargs->lmnt_preferred_hash = 0;
467
468 DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path);
469 error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/",
470 mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel());
471 if (error) {
472 panic("failed to mount locker: %d", error);
473 }
f427ee49 474 kheap_free(KHEAP_TEMP, mntargs, sizeof(*mntargs));
cb323159
A
475
476 /* Clear the old mount association. */
477 old_rootvnode->v_mountedhere = NULL;
478 rootvnode->v_mount->mnt_vnodecovered = NULL;
479 }
480#endif
481 else {
482 panic("invalid imageboot type: %d", type);
6d2010ae 483 }
2d21ac55 484
6d2010ae
A
485 /*
486 * Get the vnode for '/'.
487 * Set fdp->fd_fd.fd_cdir to reference it.
488 */
0a7de745 489 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
6d2010ae 490 panic("%s: cannot find root vnode", __FUNCTION__);
0a7de745 491 }
cb323159 492 DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name);
2d21ac55 493
cb323159 494 if (old_rootvnode != NULL) {
6d2010ae 495 /* remember the old rootvnode, but remove it from mountlist */
cb323159 496 mount_t old_rootfs = old_rootvnode->v_mount;
0a7de745 497
b7266188 498 mount_list_remove(old_rootfs);
b7266188 499 mount_lock(old_rootfs);
b7266188 500 old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT;
b7266188
A
501 old_rootfs->mnt_flag &= ~MNT_ROOTFS;
502 mount_unlock(old_rootfs);
6d2010ae 503 }
b7266188 504
f427ee49
A
505 vnode_ref(newdp);
506 vnode_put(newdp);
507
508 lck_rw_lock_exclusive(rootvnode_rw_lock);
6d2010ae 509 /* switch to the new rootvnode */
cb323159
A
510 if (update_rootvnode) {
511 rootvnode = newdp;
f427ee49 512 set_fake_bootuuid(rootvnode->v_mount);
cb323159 513 }
b7266188 514
6d2010ae
A
515 new_rootfs = rootvnode->v_mount;
516 mount_lock(new_rootfs);
517 new_rootfs->mnt_flag |= MNT_ROOTFS;
518 mount_unlock(new_rootfs);
b7266188 519
6d2010ae 520 filedesc0.fd_cdir = newdp;
f427ee49
A
521 lck_rw_unlock_exclusive(rootvnode_rw_lock);
522
6d2010ae 523 DBG_TRACE("%s: root switched\n", __FUNCTION__);
b7266188 524
6d2010ae 525 if (old_rootvnode != NULL) {
b7266188 526#ifdef CONFIG_IMGSRC_ACCESS
0a7de745
A
527 if (height >= 0 && PE_imgsrc_mount_supported()) {
528 imgsrc_rootvnodes[height] = old_rootvnode;
529 } else {
530 vnode_get_and_drop_always(old_rootvnode);
531 }
532#else
cb323159 533#pragma unused(height)
6d2010ae 534 vnode_get_and_drop_always(old_rootvnode);
b7266188 535#endif /* CONFIG_IMGSRC_ACCESS */
6d2010ae
A
536 }
537 return 0;
538}
b7266188 539
f427ee49
A
540/*
541 * Return a memory object for given file path.
542 * Also returns a vnode reference for the given file path.
543 */
544void *
545ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size)
546{
547 int err = 0;
548 struct nameidata ndp = {};
549 struct vnode *vp = NULL;
550 off_t fsize = 0;
551 vfs_context_t ctx = vfs_context_kernel();
552 void *control = NULL;
553
554 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(filename), ctx);
555 if ((err = namei(&ndp)) != 0) {
556 goto errorout;
557 }
558 nameidone(&ndp);
559 vp = ndp.ni_vp;
560
561 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
562 goto errorout;
563 }
564
565 if (fsize < 0) {
566 goto errorout;
567 }
568
569 control = ubc_getobject(vp, UBC_FLAGS_NONE);
570 if (control == NULL) {
571 goto errorout;
572 }
573
574 *file_size = fsize;
575 *vpp = vp;
576 vp = NULL;
577
578errorout:
579 if (vp) {
580 vnode_put(vp);
581 }
582 return control;
583}
584
cb323159 585int
f427ee49 586imageboot_read_file_from_offset(kalloc_heap_t kheap, const char *path, off_t offset, void **bufp, size_t *bufszp)
813fb2f6
A
587{
588 int err = 0;
589 struct nameidata ndp = {};
590 struct vnode *vp = NULL;
591 off_t fsize = 0;
592 int resid = 0;
593 char *buf = NULL;
594 bool doclose = false;
595
596 vfs_context_t ctx = vfs_context_kernel();
597 proc_t p = vfs_context_proc(ctx);
598 kauth_cred_t kerncred = vfs_context_ucred(ctx);
599
5ba3f43e 600 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
813fb2f6 601 if ((err = namei(&ndp)) != 0) {
cb323159 602 AUTHPRNT("namei failed (%s) - %d", path, err);
813fb2f6
A
603 goto out;
604 }
605 nameidone(&ndp);
606 vp = ndp.ni_vp;
607
608 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
cb323159 609 AUTHPRNT("failed to get vnode size of %s - %d", path, err);
813fb2f6
A
610 goto out;
611 }
612 if (fsize < 0) {
613 panic("negative file size");
614 }
615
616 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
cb323159 617 AUTHPRNT("failed to open %s - %d", path, err);
813fb2f6
A
618 goto out;
619 }
620 doclose = true;
621
622 /* if bufsz is non-zero, cap the read at bufsz bytes */
623 if (*bufszp && *bufszp < (size_t)fsize) {
624 fsize = *bufszp;
625 }
626
f427ee49
A
627 fsize = (off_t)MIN((size_t)fsize, INT_MAX);
628
629 buf = kheap_alloc(kheap, (size_t)fsize, Z_WAITOK);
813fb2f6
A
630 if (buf == NULL) {
631 err = ENOMEM;
632 goto out;
633 }
634
f427ee49
A
635 if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, (int)fsize, offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) {
636 AUTHPRNT("Cannot read %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, err);
813fb2f6
A
637 goto out;
638 }
639
640 if (resid) {
641 /* didnt get everything we wanted */
f427ee49 642 AUTHPRNT("Short read of %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, resid);
813fb2f6
A
643 err = EINVAL;
644 goto out;
645 }
646
647out:
648 if (doclose) {
649 VNOP_CLOSE(vp, FREAD, ctx);
650 }
651 if (vp) {
652 vnode_put(vp);
653 vp = NULL;
654 }
655
656 if (err) {
f427ee49 657 kheap_free_safe(kheap, buf, (size_t)fsize);
813fb2f6
A
658 } else {
659 *bufp = buf;
f427ee49 660 *bufszp = (size_t)fsize;
813fb2f6
A
661 }
662
663 return err;
664}
665
f427ee49
A
666int
667imageboot_read_file(kalloc_heap_t kheap, const char *path, void **bufp, size_t *bufszp)
668{
669 return imageboot_read_file_from_offset(kheap, path, 0, bufp, bufszp);
670}
671
cb323159
A
672#if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
673vnode_t
674imgboot_get_image_file(const char *path, off_t *fsize, int *errp)
813fb2f6 675{
813fb2f6 676 struct nameidata ndp = {};
cb323159 677 vnode_t vp = NULL;
813fb2f6 678 vfs_context_t ctx = vfs_context_kernel();
cb323159 679 int err;
813fb2f6 680
cb323159 681 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
813fb2f6 682 if ((err = namei(&ndp)) != 0) {
cb323159
A
683 AUTHPRNT("Cannot find %s - error %d", path, err);
684 } else {
685 nameidone(&ndp);
686 vp = ndp.ni_vp;
813fb2f6 687
cb323159 688 if (vp->v_type != VREG) {
813fb2f6 689 err = EINVAL;
cb323159
A
690 AUTHPRNT("%s it not a regular file", path);
691 } else if (fsize) {
692 if ((err = vnode_size(vp, fsize, ctx)) != 0) {
693 AUTHPRNT("Cannot get file size of %s - error %d", path, err);
694 }
813fb2f6 695 }
813fb2f6
A
696 }
697
cb323159
A
698 if (err) {
699 *errp = err;
813fb2f6
A
700 vp = NULL;
701 }
cb323159 702 return vp;
813fb2f6 703}
cb323159 704#endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
813fb2f6 705
cb323159 706#if CONFIG_IMAGEBOOT_IMG4
813fb2f6 707
cb323159 708#define APTICKET_NAME "apticket.der"
813fb2f6 709
cb323159 710static char *
f427ee49 711imgboot_get_apticket_path(const char *rootpath, size_t *sz)
cb323159 712{
f427ee49
A
713 size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME) + 1;
714 char *path = kheap_alloc(KHEAP_TEMP, plen, Z_WAITOK);
813fb2f6 715
cb323159
A
716 if (path) {
717 char *slash;
813fb2f6 718
cb323159
A
719 strlcpy(path, rootpath, plen);
720 slash = strrchr(path, '/');
721 if (slash == NULL) {
722 slash = path;
723 } else {
724 slash++;
725 }
726 strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1);
813fb2f6 727 }
f427ee49
A
728
729 *sz = plen;
cb323159 730 return path;
813fb2f6
A
731}
732
733static int
cb323159 734authenticate_root_with_img4(const char *rootpath)
813fb2f6 735{
cb323159 736 errno_t rv;
cb323159 737 vnode_t vp;
f427ee49 738 size_t ticket_pathsz = 0;
cb323159 739 char *ticket_path;
f427ee49
A
740 img4_buff_t tck = IMG4_BUFF_INIT;
741 img4_firmware_execution_context_t exec = {
742 .i4fex_version = IMG4_FIRMWARE_EXECUTION_CONTEXT_STRUCT_VERSION,
743 .i4fex_execute = NULL,
744 .i4fex_context = NULL,
745 };
746 img4_firmware_t fw = NULL;
747 img4_firmware_flags_t fw_flags = IMG4_FIRMWARE_FLAG_BARE |
748 IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE;
813fb2f6 749
cb323159 750 DBG_TRACE("Check %s\n", rootpath);
813fb2f6 751
cb323159
A
752 if (img4if == NULL) {
753 AUTHPRNT("AppleImage4 is not ready");
754 return EAGAIN;
813fb2f6
A
755 }
756
f427ee49 757 ticket_path = imgboot_get_apticket_path(rootpath, &ticket_pathsz);
cb323159
A
758 if (ticket_path == NULL) {
759 AUTHPRNT("Cannot construct ticket path - out of memory");
760 return ENOMEM;
813fb2f6
A
761 }
762
f427ee49 763 rv = imageboot_read_file(KHEAP_TEMP, ticket_path, (void **)&tck.i4b_bytes, &tck.i4b_len);
cb323159
A
764 if (rv) {
765 AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv);
766 goto out_with_ticket_path;
813fb2f6
A
767 }
768
f427ee49 769 DBG_TRACE("Got %lu bytes of manifest from %s\n", tck.i4b_len, ticket_path);
813fb2f6 770
cb323159
A
771 vp = imgboot_get_image_file(rootpath, NULL, &rv);
772 if (vp == NULL) {
773 /* Error message had been printed already */
f427ee49
A
774 rv = EIO;
775 goto out_with_ticket_bytes;
813fb2f6
A
776 }
777
f427ee49
A
778 fw = img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT, &exec, 'rosi',
779 vp, fw_flags);
780 if (!fw) {
781 AUTHPRNT("Could not allocate new firmware");
782 rv = ENOMEM;
783 goto out_with_ticket_bytes;
813fb2f6
A
784 }
785
f427ee49
A
786 img4_firmware_attach_manifest(fw, &tck);
787 rv = img4_firmware_evaluate(fw, img4_chip_select_personalized_ap(), NULL);
813fb2f6 788
cb323159 789out_with_ticket_bytes:
f427ee49 790 kheap_free_safe(KHEAP_TEMP, tck.i4b_bytes, tck.i4b_len);
cb323159 791out_with_ticket_path:
f427ee49
A
792 kheap_free_safe(KHEAP_TEMP, ticket_path, ticket_pathsz);
793
794 img4_firmware_destroy(&fw);
cb323159 795 return rv;
813fb2f6 796}
cb323159 797#endif /* CONFIG_IMAGEBOOT_IMG4 */
813fb2f6 798
813fb2f6 799
5ba3f43e
A
800/*
801 * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
802 * All existing mounts are first umounted.
803 */
804static int
805imageboot_mount_ramdisk(const char *path)
806{
807 int err = 0;
808 size_t bufsz = 0;
809 void *buf = NULL;
810 dev_t dev;
811 vnode_t newdp;
f427ee49 812 vnode_t tvp;
5ba3f43e
A
813 mount_t new_rootfs;
814
815 /* Read our target image from disk */
f427ee49 816 err = imageboot_read_file(KHEAP_DATA_BUFFERS, path, &buf, &bufsz);
5ba3f43e 817 if (err) {
f427ee49 818 printf("%s: failed: imageboot_read_file() = %d\n", __func__, err);
5ba3f43e
A
819 goto out;
820 }
821 DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz);
822
823#if CONFIG_IMGSRC_ACCESS
824 /* Re-add all root mounts to the mount list in the correct order... */
825 mount_list_remove(rootvnode->v_mount);
826 for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) {
827 struct vnode *vn = imgsrc_rootvnodes[i];
828 if (vn) {
829 vnode_getalways(vn);
830 imgsrc_rootvnodes[i] = NULLVP;
831
832 mount_t mnt = vn->v_mount;
833 mount_lock(mnt);
834 mnt->mnt_flag |= MNT_ROOTFS;
835 mount_list_add(mnt);
836 mount_unlock(mnt);
837
838 vnode_rele(vn);
839 vnode_put(vn);
840 }
841 }
842 mount_list_add(rootvnode->v_mount);
843#endif
844
845 /* ... and unmount everything */
f427ee49
A
846 vfs_unmountall();
847
848 lck_rw_lock_exclusive(rootvnode_rw_lock);
5ba3f43e 849 filedesc0.fd_cdir = NULL;
f427ee49 850 tvp = rootvnode;
5ba3f43e 851 rootvnode = NULL;
f427ee49
A
852 rootvp = NULLVP;
853 rootdev = NODEV;
854 lck_rw_unlock_exclusive(rootvnode_rw_lock);
855 vnode_get_and_drop_always(tvp);
5ba3f43e
A
856
857 /* Attach the ramfs image ... */
858 err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev);
859 if (err) {
860 printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err);
861 goto out;
862 }
863
864 /* ... and mount it */
865 rootdev = dev;
866 mountroot = NULL;
867 err = vfs_mountroot();
868 if (err) {
869 printf("%s: failed: vfs_mountroot() = %d\n", __func__, err);
870 goto out;
871 }
872
873 /* Switch to new root vnode */
0a7de745 874 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
5ba3f43e
A
875 panic("%s: cannot find root vnode", __func__);
876 }
f427ee49
A
877 vnode_ref(newdp);
878
879 lck_rw_lock_exclusive(rootvnode_rw_lock);
5ba3f43e
A
880 rootvnode = newdp;
881 rootvnode->v_flag |= VROOT;
882 new_rootfs = rootvnode->v_mount;
883 mount_lock(new_rootfs);
884 new_rootfs->mnt_flag |= MNT_ROOTFS;
885 mount_unlock(new_rootfs);
886
f427ee49
A
887 set_fake_bootuuid(new_rootfs);
888
5ba3f43e 889 filedesc0.fd_cdir = newdp;
f427ee49
A
890 lck_rw_unlock_exclusive(rootvnode_rw_lock);
891
892 vnode_put(newdp);
5ba3f43e
A
893
894 DBG_TRACE("%s: root switched\n", __func__);
895
896out:
897 if (err) {
f427ee49 898 kheap_free_safe(KHEAP_DATA_BUFFERS, buf, bufsz);
5ba3f43e
A
899 }
900 return err;
901}
902
cb323159
A
903/*
904 * If the path is in <file://> URL format then we allocate memory and decode it,
905 * otherwise return the same pointer.
906 *
907 * Caller is expected to check if the pointers are different.
908 */
909static char *
f427ee49 910url_to_path(char *url_path, size_t *sz)
cb323159
A
911{
912 char *path = url_path;
913 size_t len = strlen(kIBFilePrefix);
914
915 if (strncmp(kIBFilePrefix, url_path, len) == 0) {
916 /* its a URL - remove the file:// prefix and percent-decode */
917 url_path += len;
918
919 len = strlen(url_path);
920 if (len) {
921 /* Make a copy of the path to URL-decode */
f427ee49 922 path = kheap_alloc(KHEAP_TEMP, len + 1, Z_WAITOK);
cb323159
A
923 if (path == NULL) {
924 panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len);
925 }
926
927 strlcpy(path, url_path, len + 1);
f427ee49 928 *sz = len + 1;
cb323159
A
929 url_decode(path);
930 } else {
931 panic("Bogus imageboot path URL - missing path\n");
932 }
933
934 DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__, url_path, path);
935 }
936
937 return path;
938}
939
813fb2f6 940static boolean_t
cb323159 941imageboot_setup_new(imageboot_type_t type)
6d2010ae
A
942{
943 int error;
944 char *root_path = NULL;
945 int height = 0;
946 boolean_t done = FALSE;
cb323159 947 boolean_t auth_root = TRUE;
5ba3f43e 948 boolean_t ramdisk_root = FALSE;
6d2010ae 949
f427ee49 950 root_path = zalloc(ZV_NAMEI);
6d2010ae
A
951 assert(root_path != NULL);
952
cb323159
A
953#if CONFIG_LOCKERBOOT
954 if (type == IMAGEBOOT_LOCKER) {
955 if (!PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) {
956 panic("locker boot with no locker given");
957 }
958
959 DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__, rootvnode->v_mount->mnt_vtable->vfc_name);
960
961 /*
962 * The locker path is a path, not a URL, so just pass it directly to
963 * imageboot_mount_image().
964 */
965 error = imageboot_mount_image(root_path, 0, type);
966 if (error) {
967 panic("failed to mount system locker: %d", error);
968 }
969
970 done = TRUE;
971 goto out;
972 }
973#endif /* CONFIG_LOCKERBOOT */
974
5ba3f43e
A
975 unsigned imgboot_arg;
976 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) {
977 ramdisk_root = TRUE;
978 }
979
813fb2f6 980 if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
6d2010ae 981 printf("%s: container image url is %s\n", __FUNCTION__, root_path);
cb323159 982 error = imageboot_mount_image(root_path, height, type);
6d2010ae
A
983 if (error != 0) {
984 panic("Failed to mount container image.");
985 }
986
987 height++;
988 }
989
cb323159
A
990 if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == FALSE &&
991 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) {
6d2010ae 992 if (height > 0) {
cb323159 993 panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG);
6d2010ae
A
994 }
995 goto out;
813fb2f6
A
996 }
997
cb323159 998 printf("%s: root image URL is '%s'\n", __func__, root_path);
b7266188 999
5ba3f43e 1000 /* Make a copy of the path to URL-decode */
f427ee49
A
1001 size_t pathsz;
1002 char *path = url_to_path(root_path, &pathsz);
cb323159 1003 assert(path);
6d2010ae 1004
cb323159 1005#if CONFIG_IMAGEBOOT_CHUNKLIST
5ba3f43e 1006 if (auth_root) {
f427ee49
A
1007 /*
1008 * This updates auth_root to reflect whether chunklist was
1009 * actually enforced. In effect, this clears auth_root if
1010 * CSR_ALLOW_ANY_RECOVERY_OS allowed an invalid image.
1011 */
813fb2f6 1012 AUTHDBG("authenticating root image at %s", path);
f427ee49 1013 error = authenticate_root_with_chunklist(path, &auth_root);
813fb2f6
A
1014 if (error) {
1015 panic("root image authentication failed (err = %d)\n", error);
1016 }
1017 AUTHDBG("successfully authenticated %s", path);
5ba3f43e 1018 }
cb323159 1019#endif
00867663 1020
5ba3f43e
A
1021 if (ramdisk_root) {
1022 error = imageboot_mount_ramdisk(path);
1023 } else {
cb323159 1024 error = imageboot_mount_image(root_path, height, type);
813fb2f6 1025 }
6d2010ae 1026
cb323159 1027 if (path != root_path) {
f427ee49 1028 kheap_free_safe(KHEAP_TEMP, path, pathsz);
cb323159 1029 }
5ba3f43e
A
1030
1031 if (error) {
1032 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
0a7de745 1033 error, auth_root, ramdisk_root);
6d2010ae
A
1034 }
1035
cb323159 1036#if CONFIG_IMAGEBOOT_CHUNKLIST
813fb2f6
A
1037 if (auth_root) {
1038 /* check that the image version matches the running kernel */
1039 AUTHDBG("checking root image version");
cb323159 1040 error = authenticate_root_version_check();
813fb2f6
A
1041 if (error) {
1042 panic("root image version check failed");
1043 } else {
1044 AUTHDBG("root image version matches kernel");
1045 }
1046 }
cb323159 1047#endif
813fb2f6 1048
6d2010ae
A
1049 done = TRUE;
1050
1051out:
f427ee49 1052 zfree(ZV_NAMEI, root_path);
6d2010ae
A
1053 return done;
1054}
1055
1056__private_extern__ void
cb323159 1057imageboot_setup(imageboot_type_t type)
6d2010ae
A
1058{
1059 int error = 0;
1060 char *root_path = NULL;
1061
1062 DBG_TRACE("%s: entry\n", __FUNCTION__);
1063
813fb2f6 1064 if (rootvnode == NULL) {
6d2010ae
A
1065 panic("imageboot_setup: rootvnode is NULL.");
1066 }
1067
1068 /*
1069 * New boot-arg scheme:
cb323159
A
1070 * root-dmg : the dmg that will be the root filesystem, authenticated by default.
1071 * auth-root-dmg : same as root-dmg.
0a7de745 1072 * container-dmg : an optional dmg that contains the root-dmg.
cb323159
A
1073 * locker : the locker that will be the root filesystem -- mutually
1074 * exclusive with any other boot-arg.
6d2010ae 1075 */
cb323159 1076 if (imageboot_setup_new(type)) {
6d2010ae
A
1077 return;
1078 }
813fb2f6 1079
f427ee49 1080 root_path = zalloc(ZV_NAMEI);
6d2010ae
A
1081 assert(root_path != NULL);
1082
1083 /*
1084 * Look for outermost disk image to root from. If we're doing a nested boot,
1085 * there's some sense in which the outer image never needs to be the root filesystem,
1086 * but it does need very similar treatment: it must not be unmounted, needs a fake
0a7de745 1087 * device vnode created for it, and should not show up in getfsstat() until exposed
6d2010ae
A
1088 * with MNT_IMGSRC. We just make it the temporary root.
1089 */
cb323159
A
1090#if CONFIG_IMAGEBOOT_IMG4
1091 if (PE_parse_boot_argn("arp0", root_path, MAXPATHLEN)) {
f427ee49
A
1092 size_t pathsz;
1093 char *path = url_to_path(root_path, &pathsz);
cb323159
A
1094
1095 assert(path);
1096
1097 if (authenticate_root_with_img4(path)) {
1098 panic("Root image %s does not match the manifest\n", root_path);
1099 }
1100 if (path != root_path) {
f427ee49 1101 kheap_free_safe(KHEAP_TEMP, path, pathsz);
cb323159
A
1102 }
1103 } else
1104#endif /* CONFIG_IMAGEBOOT_IMG4 */
0a7de745
A
1105 if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
1106 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
6d2010ae
A
1107 panic("%s: no valid path to image.\n", __FUNCTION__);
1108 }
1109
cb323159 1110 DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path);
0a7de745 1111
cb323159 1112 error = imageboot_mount_image(root_path, 0, type);
6d2010ae
A
1113 if (error) {
1114 panic("Failed on first stage of imageboot.");
1115 }
1116
1117 /*
1118 * See if we are rooting from a nested image
1119 */
0a7de745 1120 if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
6d2010ae
A
1121 goto done;
1122 }
0a7de745 1123
6d2010ae
A
1124 printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
1125
1126 /*
1127 * If we fail to set up second image, it's not a given that we
0a7de745 1128 * can safely root off the first.
6d2010ae 1129 */
cb323159 1130 error = imageboot_mount_image(root_path, 1, type);
6d2010ae 1131 if (error) {
0a7de745 1132 panic("Failed on second stage of imageboot.");
6d2010ae
A
1133 }
1134
2d21ac55 1135done:
f427ee49 1136 zfree(ZV_NAMEI, root_path);
2d21ac55
A
1137
1138 DBG_TRACE("%s: exit\n", __FUNCTION__);
1139
6d2010ae 1140 return;
2d21ac55 1141}