]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/imageboot.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
1 /*
2 * Copyright (c) 2006-2020 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
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>
35 #include <sys/fsctl.h>
36 #include <sys/filedesc.h>
37 #include <sys/vnode_internal.h>
38 #include <sys/imageboot.h>
39 #include <kern/assert.h>
40
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>
46 #include <miscfs/devfs/devfsdefs.h>
47 #include <libkern/crypto/sha2.h>
48 #include <libkern/crypto/rsa.h>
49 #include <libkern/OSKextLibPrivate.h>
50 #include <sys/ubc_internal.h>
51
52 #if CONFIG_IMAGEBOOT_IMG4
53 #include <libkern/img4/interface.h>
54 #include <img4/firmware.h>
55 #endif
56
57 #include <kern/kalloc.h>
58
59 #include <pexpert/pexpert.h>
60 #include <kern/chunklist.h>
61
62 extern struct filedesc filedesc0;
63
64 extern int (*mountroot)(void);
65 extern char rootdevice[DEVMAXNAMESIZE];
66
67 #if CONFIG_LOCKERBOOT
68 typedef struct _locker_mount_args {
69 char lmnt_path[PATH_MAX];
70 uint16_t lmnt_preferred_hash;
71 } locker_mount_args_t;
72 #endif
73
74 #define DEBUG_IMAGEBOOT 0
75
76 #if DEBUG_IMAGEBOOT
77 #define DBG_TRACE(...) printf("imageboot: " __VA_ARGS__)
78 #else
79 #define DBG_TRACE(...) do {} while(0)
80 #endif
81
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)
84 #define kheap_free_safe(h, x, l) do { if ((x)) { kheap_free(h, x, l); (x) = NULL; } } while (0)
85
86 extern int di_root_image_ext(const char *path, char *devname, size_t devsz, dev_t *dev_p, bool removable);
87 extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p);
88 extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p);
89
90 static boolean_t imageboot_setup_new(imageboot_type_t type);
91
92 void *ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size);
93
94 extern lck_rw_t rootvnode_rw_lock;
95
96 #define kIBFilePrefix "file://"
97
98 __private_extern__ int
99 imageboot_format_is_valid(const char *root_path)
100 {
101 return strncmp(root_path, kIBFilePrefix,
102 strlen(kIBFilePrefix)) == 0;
103 }
104
105 static void
106 vnode_get_and_drop_always(vnode_t vp)
107 {
108 vnode_getalways(vp);
109 vnode_rele(vp);
110 vnode_put(vp);
111 }
112
113 __private_extern__ bool
114 imageboot_desired(void)
115 {
116 bool do_imageboot = false;
117
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 */
132 if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
133 #if CONFIG_IMAGEBOOT_IMG4
134 PE_parse_boot_argn("arp0", root_path, MAXPATHLEN) ||
135 #endif
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))) {
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 }
151 }
152
153 zfree(ZV_NAMEI, root_path);
154 return do_imageboot;
155 }
156
157 __private_extern__ imageboot_type_t
158 imageboot_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()) {
166 goto out;
167 }
168
169 root_path = zalloc(ZV_NAMEI);
170 result = IMAGEBOOT_DMG;
171
172 /* Check for second layer */
173 if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
174 PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
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",
183 __FUNCTION__, root_path);
184 }
185
186 out:
187 if (root_path != NULL) {
188 zfree(ZV_NAMEI, root_path);
189 }
190 return result;
191 }
192
193 extern 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 `skip_signature_check` is true,
203 * then ignore the chunklisted or authAPFS checks on this image
204 */
205 __private_extern__ int
206 imageboot_pivot_image(const char *image_path, imageboot_type_t type, const char *mount_path,
207 const char *outgoing_root_path, const bool rooted_dmg, const bool skip_signature_check)
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 * Or, if CSR_ALLOW_ANY_RECOVERY_OS is set on Development or Debug build variant.
328 */
329 if (rooted_dmg || authenticated_dmg_chunklist || skip_signature_check) {
330 rootauth = 0;
331 }
332 error = rootauth;
333 }
334 vnode_put(mount_vp);
335 mount_vp = NULLVP;
336
337 if (error) {
338 /*
339 * Failure here exclusively means that the mount failed to authenticate.
340 * This means that the disk image either was not sealed (authapfs), or it was
341 * not hosted on a chunklisted DMG. Both scenarios may be fatal depending
342 * on the platform.
343 */
344 #if defined(__arm64__) && XNU_TARGET_OS_OSX
345 panic("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error);
346 #endif
347 printf("%s: could not authenticate the pivot image: %d. giving up.\n", __FUNCTION__, error);
348 goto done;
349 }
350
351 if (rootvnode) {
352 mount_t root_mp = vnode_mount(rootvnode);
353 if (root_mp && (root_mp->mnt_kern_flag & MNTK_SSD)) {
354 rootvp_is_ssd = true;
355 }
356 }
357 /*
358 * pivot the incoming and outgoing filesystems
359 */
360 error = vfs_switch_root(mount_path, outgoing_root_path, 0);
361 if (error) {
362 panic("%s: vfs_switch_root failed: %d\n", __FUNCTION__, error);
363 }
364
365 /*
366 * Mark the filesystem containing the image as backing root, so it
367 * won't be unmountable.
368 *
369 * vfs_switch_root() clears this flag, so we have to set it after
370 * the pivot call.
371 * If the system later pivots out of the image, vfs_switch_root
372 * will clear it again, so the backing filesystem can be unmounted.
373 */
374 mount_t imagemp = imagevp->v_mount;
375 lck_rw_lock_exclusive(&imagemp->mnt_rwlock);
376 imagemp->mnt_kern_flag |= MNTK_BACKS_ROOT;
377 lck_rw_done(&imagemp->mnt_rwlock);
378
379 error = 0;
380
381 /*
382 * Note that we do NOT change kern.bootuuid here -
383 * imageboot_mount_image() does, but imageboot_pivot_image() doesn't.
384 * imageboot_mount_image() is used when the root volume uuid was
385 * "always supposed to be" the one inside the dmg. imageboot_pivot_
386 * image() is used when the true root volume just needs to be
387 * obscured for a moment by the dmg.
388 */
389
390 done:
391 if (imagevp != NULLVP) {
392 vnode_put(imagevp);
393 }
394 return error;
395 }
396
397 /* kern_sysctl.c */
398 extern uuid_string_t fake_bootuuid;
399
400 static void
401 set_fake_bootuuid(mount_t mp)
402 {
403 struct vfs_attr va;
404 VFSATTR_INIT(&va);
405 VFSATTR_WANTED(&va, f_uuid);
406
407 if (vfs_getattr(mp, &va, vfs_context_current()) != 0) {
408 return;
409 }
410
411 if (!VFSATTR_IS_SUPPORTED(&va, f_uuid)) {
412 return;
413 }
414
415 uuid_unparse(va.f_uuid, fake_bootuuid);
416 }
417
418 /*
419 * Swaps in new root filesystem based on image path.
420 * Current root filesystem is removed from mount list and
421 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
422 * "rootvnode" is reset. Root vnode of currentroot filesystem
423 * is returned with usecount (no iocount).
424 * kern.bootuuid is arranged to return the UUID of the mounted image. (If
425 * we did nothing here, it would be the UUID of the image source volume.)
426 */
427 __private_extern__ int
428 imageboot_mount_image(const char *root_path, int height, imageboot_type_t type)
429 {
430 dev_t dev;
431 int error;
432 /*
433 * Need to stash this here since we may do a kernel_mount() on /, which will
434 * automatically update the rootvnode global. Note that vfs_mountroot() does
435 * not update that global, which is a bit weird.
436 */
437 vnode_t old_rootvnode = rootvnode;
438 vnode_t newdp;
439 mount_t new_rootfs;
440 boolean_t update_rootvnode = FALSE;
441
442 if (type == IMAGEBOOT_DMG) {
443 error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev);
444 if (error) {
445 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
446 }
447
448 rootdev = dev;
449 mountroot = NULL;
450 printf("%s: root device 0x%x\n", __FUNCTION__, rootdev);
451 error = vfs_mountroot();
452 if (error != 0) {
453 panic("vfs_mountroot() failed.\n");
454 }
455
456 update_rootvnode = TRUE;
457 }
458 #if CONFIG_LOCKERBOOT
459 else if (type == IMAGEBOOT_LOCKER) {
460 locker_mount_args_t *mntargs = kheap_alloc(KHEAP_TEMP,
461 sizeof(*mntargs), Z_WAITOK);
462 if (!mntargs) {
463 panic("could not alloc mount args");
464 }
465
466 strlcpy(mntargs->lmnt_path, root_path, sizeof(mntargs->lmnt_path));
467 mntargs->lmnt_preferred_hash = 0;
468
469 DBG_TRACE("%s: mounting locker: %s\n", __FUNCTION__, root_path);
470 error = kernel_mount(LOCKERFS_NAME, NULLVP, NULLVP, "/",
471 mntargs, sizeof(*mntargs), 0, 0, vfs_context_kernel());
472 if (error) {
473 panic("failed to mount locker: %d", error);
474 }
475 kheap_free(KHEAP_TEMP, mntargs, sizeof(*mntargs));
476
477 /* Clear the old mount association. */
478 old_rootvnode->v_mountedhere = NULL;
479 rootvnode->v_mount->mnt_vnodecovered = NULL;
480 }
481 #endif
482 else {
483 panic("invalid imageboot type: %d", type);
484 }
485
486 /*
487 * Get the vnode for '/'.
488 * Set fdp->fd_fd.fd_cdir to reference it.
489 */
490 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
491 panic("%s: cannot find root vnode", __FUNCTION__);
492 }
493 DBG_TRACE("%s: old root fsname: %s\n", __FUNCTION__, old_rootvnode->v_mount->mnt_vtable->vfc_name);
494
495 if (old_rootvnode != NULL) {
496 /* remember the old rootvnode, but remove it from mountlist */
497 mount_t old_rootfs = old_rootvnode->v_mount;
498
499 mount_list_remove(old_rootfs);
500 mount_lock(old_rootfs);
501 old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT;
502 old_rootfs->mnt_flag &= ~MNT_ROOTFS;
503 mount_unlock(old_rootfs);
504 }
505
506 vnode_ref(newdp);
507 vnode_put(newdp);
508
509 lck_rw_lock_exclusive(&rootvnode_rw_lock);
510 /* switch to the new rootvnode */
511 if (update_rootvnode) {
512 rootvnode = newdp;
513 set_fake_bootuuid(rootvnode->v_mount);
514 }
515
516 new_rootfs = rootvnode->v_mount;
517 mount_lock(new_rootfs);
518 new_rootfs->mnt_flag |= MNT_ROOTFS;
519 mount_unlock(new_rootfs);
520
521 filedesc0.fd_cdir = newdp;
522 lck_rw_unlock_exclusive(&rootvnode_rw_lock);
523
524 DBG_TRACE("%s: root switched\n", __FUNCTION__);
525
526 if (old_rootvnode != NULL) {
527 #ifdef CONFIG_IMGSRC_ACCESS
528 if (height >= 0 && PE_imgsrc_mount_supported()) {
529 imgsrc_rootvnodes[height] = old_rootvnode;
530 } else {
531 vnode_get_and_drop_always(old_rootvnode);
532 }
533 #else
534 #pragma unused(height)
535 vnode_get_and_drop_always(old_rootvnode);
536 #endif /* CONFIG_IMGSRC_ACCESS */
537 }
538 return 0;
539 }
540
541 /*
542 * Return a memory object for given file path.
543 * Also returns a vnode reference for the given file path.
544 */
545 void *
546 ubc_getobject_from_filename(const char *filename, struct vnode **vpp, off_t *file_size)
547 {
548 int err = 0;
549 struct nameidata ndp = {};
550 struct vnode *vp = NULL;
551 off_t fsize = 0;
552 vfs_context_t ctx = vfs_context_kernel();
553 void *control = NULL;
554
555 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(filename), ctx);
556 if ((err = namei(&ndp)) != 0) {
557 goto errorout;
558 }
559 nameidone(&ndp);
560 vp = ndp.ni_vp;
561
562 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
563 goto errorout;
564 }
565
566 if (fsize < 0) {
567 goto errorout;
568 }
569
570 control = ubc_getobject(vp, UBC_FLAGS_NONE);
571 if (control == NULL) {
572 goto errorout;
573 }
574
575 *file_size = fsize;
576 *vpp = vp;
577 vp = NULL;
578
579 errorout:
580 if (vp) {
581 vnode_put(vp);
582 }
583 return control;
584 }
585
586 int
587 imageboot_read_file_from_offset(kalloc_heap_t kheap, const char *path, off_t offset, void **bufp, size_t *bufszp)
588 {
589 int err = 0;
590 struct nameidata ndp = {};
591 struct vnode *vp = NULL;
592 off_t fsize = 0;
593 int resid = 0;
594 char *buf = NULL;
595 bool doclose = false;
596
597 vfs_context_t ctx = vfs_context_kernel();
598 proc_t p = vfs_context_proc(ctx);
599 kauth_cred_t kerncred = vfs_context_ucred(ctx);
600
601 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
602 if ((err = namei(&ndp)) != 0) {
603 AUTHPRNT("namei failed (%s) - %d", path, err);
604 goto out;
605 }
606 nameidone(&ndp);
607 vp = ndp.ni_vp;
608
609 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
610 AUTHPRNT("failed to get vnode size of %s - %d", path, err);
611 goto out;
612 }
613 if (fsize < 0) {
614 panic("negative file size");
615 }
616
617 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
618 AUTHPRNT("failed to open %s - %d", path, err);
619 goto out;
620 }
621 doclose = true;
622
623 /* if bufsz is non-zero, cap the read at bufsz bytes */
624 if (*bufszp && *bufszp < (size_t)fsize) {
625 fsize = *bufszp;
626 }
627
628 fsize = (off_t)MIN((size_t)fsize, INT_MAX);
629
630 buf = kheap_alloc(kheap, (size_t)fsize, Z_WAITOK);
631 if (buf == NULL) {
632 err = ENOMEM;
633 goto out;
634 }
635
636 if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, (int)fsize, offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) {
637 AUTHPRNT("Cannot read %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, err);
638 goto out;
639 }
640
641 if (resid) {
642 /* didnt get everything we wanted */
643 AUTHPRNT("Short read of %d bytes at offset %d from %s - %d", (int)fsize, (int)offset, path, resid);
644 err = EINVAL;
645 goto out;
646 }
647
648 out:
649 if (doclose) {
650 VNOP_CLOSE(vp, FREAD, ctx);
651 }
652 if (vp) {
653 vnode_put(vp);
654 vp = NULL;
655 }
656
657 if (err) {
658 kheap_free_safe(kheap, buf, (size_t)fsize);
659 } else {
660 *bufp = buf;
661 *bufszp = (size_t)fsize;
662 }
663
664 return err;
665 }
666
667 int
668 imageboot_read_file(kalloc_heap_t kheap, const char *path, void **bufp, size_t *bufszp)
669 {
670 return imageboot_read_file_from_offset(kheap, path, 0, bufp, bufszp);
671 }
672
673 #if CONFIG_IMAGEBOOT_IMG4 || CONFIG_IMAGEBOOT_CHUNKLIST
674 vnode_t
675 imgboot_get_image_file(const char *path, off_t *fsize, int *errp)
676 {
677 struct nameidata ndp = {};
678 vnode_t vp = NULL;
679 vfs_context_t ctx = vfs_context_kernel();
680 int err;
681
682 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
683 if ((err = namei(&ndp)) != 0) {
684 AUTHPRNT("Cannot find %s - error %d", path, err);
685 } else {
686 nameidone(&ndp);
687 vp = ndp.ni_vp;
688
689 if (vp->v_type != VREG) {
690 err = EINVAL;
691 AUTHPRNT("%s it not a regular file", path);
692 } else if (fsize) {
693 if ((err = vnode_size(vp, fsize, ctx)) != 0) {
694 AUTHPRNT("Cannot get file size of %s - error %d", path, err);
695 }
696 }
697 }
698
699 if (err) {
700 if (vp) {
701 vnode_put(vp);
702 }
703 *errp = err;
704 vp = NULL;
705 }
706 return vp;
707 }
708 #endif /* CONFIG_IMAGEBOOT_CHUNKLIST || CONFIG_IMAGEBOOT_CHUNKLIST */
709
710 #if CONFIG_IMAGEBOOT_IMG4
711
712 #define APTICKET_NAME "apticket.der"
713
714 static char *
715 imgboot_get_apticket_path(const char *rootpath, size_t *sz)
716 {
717 size_t plen = strlen(rootpath) + sizeof(APTICKET_NAME) + 1;
718 char *path = kheap_alloc(KHEAP_TEMP, plen, Z_WAITOK);
719
720 if (path) {
721 char *slash;
722
723 strlcpy(path, rootpath, plen);
724 slash = strrchr(path, '/');
725 if (slash == NULL) {
726 slash = path;
727 } else {
728 slash++;
729 }
730 strlcpy(slash, APTICKET_NAME, sizeof(APTICKET_NAME) + 1);
731 }
732
733 *sz = plen;
734 return path;
735 }
736
737 static int
738 authenticate_root_with_img4(const char *rootpath)
739 {
740 errno_t rv;
741 vnode_t vp;
742 size_t ticket_pathsz = 0;
743 char *ticket_path;
744 img4_buff_t tck = IMG4_BUFF_INIT;
745 img4_firmware_execution_context_t exec = {
746 .i4fex_version = IMG4_FIRMWARE_EXECUTION_CONTEXT_STRUCT_VERSION,
747 .i4fex_execute = NULL,
748 .i4fex_context = NULL,
749 };
750 img4_firmware_t fw = NULL;
751 img4_firmware_flags_t fw_flags = IMG4_FIRMWARE_FLAG_BARE |
752 IMG4_FIRMWARE_FLAG_SUBSEQUENT_STAGE;
753
754 DBG_TRACE("Check %s\n", rootpath);
755
756 if (img4if == NULL) {
757 AUTHPRNT("AppleImage4 is not ready");
758 return EAGAIN;
759 }
760
761 ticket_path = imgboot_get_apticket_path(rootpath, &ticket_pathsz);
762 if (ticket_path == NULL) {
763 AUTHPRNT("Cannot construct ticket path - out of memory");
764 return ENOMEM;
765 }
766
767 rv = imageboot_read_file(KHEAP_TEMP, ticket_path, (void **)&tck.i4b_bytes, &tck.i4b_len);
768 if (rv) {
769 AUTHPRNT("Cannot get a ticket from %s - %d\n", ticket_path, rv);
770 goto out_with_ticket_path;
771 }
772
773 DBG_TRACE("Got %lu bytes of manifest from %s\n", tck.i4b_len, ticket_path);
774
775 vp = imgboot_get_image_file(rootpath, NULL, &rv);
776 if (vp == NULL) {
777 /* Error message had been printed already */
778 rv = EIO;
779 goto out_with_ticket_bytes;
780 }
781
782 fw = img4_firmware_new_from_vnode_4xnu(IMG4_RUNTIME_DEFAULT, &exec, 'rosi',
783 vp, fw_flags);
784 if (!fw) {
785 AUTHPRNT("Could not allocate new firmware");
786 rv = ENOMEM;
787 goto out_with_ticket_bytes;
788 }
789
790 img4_firmware_attach_manifest(fw, &tck);
791 rv = img4_firmware_evaluate(fw, img4_chip_select_personalized_ap(), NULL);
792
793 out_with_ticket_bytes:
794 kheap_free_safe(KHEAP_TEMP, tck.i4b_bytes, tck.i4b_len);
795 out_with_ticket_path:
796 kheap_free_safe(KHEAP_TEMP, ticket_path, ticket_pathsz);
797
798 img4_firmware_destroy(&fw);
799 return rv;
800 }
801 #endif /* CONFIG_IMAGEBOOT_IMG4 */
802
803
804 /*
805 * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
806 * All existing mounts are first umounted.
807 */
808 static int
809 imageboot_mount_ramdisk(const char *path)
810 {
811 int err = 0;
812 size_t bufsz = 0;
813 void *buf = NULL;
814 dev_t dev;
815 vnode_t newdp;
816 vnode_t tvp;
817 mount_t new_rootfs;
818
819 /* Read our target image from disk */
820 err = imageboot_read_file(KHEAP_DATA_BUFFERS, path, &buf, &bufsz);
821 if (err) {
822 printf("%s: failed: imageboot_read_file() = %d\n", __func__, err);
823 goto out;
824 }
825 DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz);
826
827 #if CONFIG_IMGSRC_ACCESS
828 /* Re-add all root mounts to the mount list in the correct order... */
829 mount_list_remove(rootvnode->v_mount);
830 for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) {
831 struct vnode *vn = imgsrc_rootvnodes[i];
832 if (vn) {
833 vnode_getalways(vn);
834 imgsrc_rootvnodes[i] = NULLVP;
835
836 mount_t mnt = vn->v_mount;
837 mount_lock(mnt);
838 mnt->mnt_flag |= MNT_ROOTFS;
839 mount_list_add(mnt);
840 mount_unlock(mnt);
841
842 vnode_rele(vn);
843 vnode_put(vn);
844 }
845 }
846 mount_list_add(rootvnode->v_mount);
847 #endif
848
849 /* ... and unmount everything */
850 vfs_unmountall(FALSE);
851
852 lck_rw_lock_exclusive(&rootvnode_rw_lock);
853 filedesc0.fd_cdir = NULL;
854 tvp = rootvnode;
855 rootvnode = NULL;
856 rootvp = NULLVP;
857 rootdev = NODEV;
858 lck_rw_unlock_exclusive(&rootvnode_rw_lock);
859 vnode_get_and_drop_always(tvp);
860
861 /* Attach the ramfs image ... */
862 err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev);
863 if (err) {
864 printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err);
865 goto out;
866 }
867
868 /* ... and mount it */
869 rootdev = dev;
870 mountroot = NULL;
871 err = vfs_mountroot();
872 if (err) {
873 printf("%s: failed: vfs_mountroot() = %d\n", __func__, err);
874 goto out;
875 }
876
877 /* Switch to new root vnode */
878 if (VFS_ROOT(TAILQ_LAST(&mountlist, mntlist), &newdp, vfs_context_kernel())) {
879 panic("%s: cannot find root vnode", __func__);
880 }
881 vnode_ref(newdp);
882
883 lck_rw_lock_exclusive(&rootvnode_rw_lock);
884 rootvnode = newdp;
885 rootvnode->v_flag |= VROOT;
886 new_rootfs = rootvnode->v_mount;
887 mount_lock(new_rootfs);
888 new_rootfs->mnt_flag |= MNT_ROOTFS;
889 mount_unlock(new_rootfs);
890
891 set_fake_bootuuid(new_rootfs);
892
893 filedesc0.fd_cdir = newdp;
894 lck_rw_unlock_exclusive(&rootvnode_rw_lock);
895
896 vnode_put(newdp);
897
898 DBG_TRACE("%s: root switched\n", __func__);
899
900 out:
901 if (err) {
902 kheap_free_safe(KHEAP_DATA_BUFFERS, buf, bufsz);
903 }
904 return err;
905 }
906
907 /*
908 * If the path is in <file://> URL format then we allocate memory and decode it,
909 * otherwise return the same pointer.
910 *
911 * Caller is expected to check if the pointers are different.
912 */
913 static char *
914 url_to_path(char *url_path, size_t *sz)
915 {
916 char *path = url_path;
917 size_t len = strlen(kIBFilePrefix);
918
919 if (strncmp(kIBFilePrefix, url_path, len) == 0) {
920 /* its a URL - remove the file:// prefix and percent-decode */
921 url_path += len;
922
923 len = strlen(url_path);
924 if (len) {
925 /* Make a copy of the path to URL-decode */
926 path = kheap_alloc(KHEAP_TEMP, len + 1, Z_WAITOK);
927 if (path == NULL) {
928 panic("imageboot path allocation failed - cannot allocate %d bytes\n", (int)len);
929 }
930
931 strlcpy(path, url_path, len + 1);
932 *sz = len + 1;
933 url_decode(path);
934 } else {
935 panic("Bogus imageboot path URL - missing path\n");
936 }
937
938 DBG_TRACE("%s: root image URL <%s> becomes %s\n", __func__, url_path, path);
939 }
940
941 return path;
942 }
943
944 static boolean_t
945 imageboot_setup_new(imageboot_type_t type)
946 {
947 int error;
948 char *root_path = NULL;
949 int height = 0;
950 boolean_t done = FALSE;
951 boolean_t auth_root = TRUE;
952 boolean_t ramdisk_root = FALSE;
953
954 root_path = zalloc(ZV_NAMEI);
955 assert(root_path != NULL);
956
957 #if CONFIG_LOCKERBOOT
958 if (type == IMAGEBOOT_LOCKER) {
959 if (!PE_parse_boot_argn(IMAGEBOOT_LOCKER_ARG, root_path, MAXPATHLEN)) {
960 panic("locker boot with no locker given");
961 }
962
963 DBG_TRACE("%s: root fsname: %s\n", __FUNCTION__, rootvnode->v_mount->mnt_vtable->vfc_name);
964
965 /*
966 * The locker path is a path, not a URL, so just pass it directly to
967 * imageboot_mount_image().
968 */
969 error = imageboot_mount_image(root_path, 0, type);
970 if (error) {
971 panic("failed to mount system locker: %d", error);
972 }
973
974 done = TRUE;
975 goto out;
976 }
977 #endif /* CONFIG_LOCKERBOOT */
978
979 unsigned imgboot_arg;
980 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) {
981 ramdisk_root = TRUE;
982 }
983
984 if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
985 printf("%s: container image url is %s\n", __FUNCTION__, root_path);
986 error = imageboot_mount_image(root_path, height, type);
987 if (error != 0) {
988 panic("Failed to mount container image.");
989 }
990
991 height++;
992 }
993
994 if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == FALSE &&
995 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) {
996 if (height > 0) {
997 panic("%s specified without %s or %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_AUTHROOT_ARG, IMAGEBOOT_ROOT_ARG);
998 }
999 goto out;
1000 }
1001
1002 printf("%s: root image URL is '%s'\n", __func__, root_path);
1003
1004 /* Make a copy of the path to URL-decode */
1005 size_t pathsz;
1006 char *path = url_to_path(root_path, &pathsz);
1007 assert(path);
1008
1009 #if CONFIG_IMAGEBOOT_CHUNKLIST
1010 if (auth_root) {
1011 /*
1012 * This updates auth_root to reflect whether chunklist was
1013 * actually enforced. In effect, this clears auth_root if
1014 * CSR_ALLOW_ANY_RECOVERY_OS allowed an invalid image.
1015 */
1016 AUTHDBG("authenticating root image at %s", path);
1017 error = authenticate_root_with_chunklist(path, &auth_root);
1018 if (error) {
1019 panic("root image authentication failed (err = %d)\n", error);
1020 }
1021 AUTHDBG("successfully authenticated %s", path);
1022 }
1023 #endif
1024
1025 if (ramdisk_root) {
1026 error = imageboot_mount_ramdisk(path);
1027 } else {
1028 error = imageboot_mount_image(root_path, height, type);
1029 }
1030
1031 if (path != root_path) {
1032 kheap_free_safe(KHEAP_TEMP, path, pathsz);
1033 }
1034
1035 if (error) {
1036 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
1037 error, auth_root, ramdisk_root);
1038 }
1039
1040 #if CONFIG_IMAGEBOOT_CHUNKLIST
1041 if (auth_root) {
1042 /* check that the image version matches the running kernel */
1043 AUTHDBG("checking root image version");
1044 error = authenticate_root_version_check();
1045 if (error) {
1046 panic("root image version check failed");
1047 } else {
1048 AUTHDBG("root image version matches kernel");
1049 }
1050 }
1051 #endif
1052
1053 done = TRUE;
1054
1055 out:
1056 zfree(ZV_NAMEI, root_path);
1057 return done;
1058 }
1059
1060 __private_extern__ void
1061 imageboot_setup(imageboot_type_t type)
1062 {
1063 int error = 0;
1064 char *root_path = NULL;
1065
1066 DBG_TRACE("%s: entry\n", __FUNCTION__);
1067
1068 if (rootvnode == NULL) {
1069 panic("imageboot_setup: rootvnode is NULL.");
1070 }
1071
1072 /*
1073 * New boot-arg scheme:
1074 * root-dmg : the dmg that will be the root filesystem, authenticated by default.
1075 * auth-root-dmg : same as root-dmg.
1076 * container-dmg : an optional dmg that contains the root-dmg.
1077 * locker : the locker that will be the root filesystem -- mutually
1078 * exclusive with any other boot-arg.
1079 */
1080 if (imageboot_setup_new(type)) {
1081 return;
1082 }
1083
1084 root_path = zalloc(ZV_NAMEI);
1085 assert(root_path != NULL);
1086
1087 /*
1088 * Look for outermost disk image to root from. If we're doing a nested boot,
1089 * there's some sense in which the outer image never needs to be the root filesystem,
1090 * but it does need very similar treatment: it must not be unmounted, needs a fake
1091 * device vnode created for it, and should not show up in getfsstat() until exposed
1092 * with MNT_IMGSRC. We just make it the temporary root.
1093 */
1094 #if CONFIG_IMAGEBOOT_IMG4
1095 if (PE_parse_boot_argn("arp0", root_path, MAXPATHLEN)) {
1096 size_t pathsz;
1097 char *path = url_to_path(root_path, &pathsz);
1098
1099 assert(path);
1100
1101 if (authenticate_root_with_img4(path)) {
1102 panic("Root image %s does not match the manifest\n", root_path);
1103 }
1104 if (path != root_path) {
1105 kheap_free_safe(KHEAP_TEMP, path, pathsz);
1106 }
1107 } else
1108 #endif /* CONFIG_IMAGEBOOT_IMG4 */
1109 if ((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
1110 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
1111 panic("%s: no valid path to image.\n", __FUNCTION__);
1112 }
1113
1114 DBG_TRACE("%s: root image url is %s\n", __FUNCTION__, root_path);
1115
1116 error = imageboot_mount_image(root_path, 0, type);
1117 if (error) {
1118 panic("Failed on first stage of imageboot.");
1119 }
1120
1121 /*
1122 * See if we are rooting from a nested image
1123 */
1124 if (PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
1125 goto done;
1126 }
1127
1128 printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
1129
1130 /*
1131 * If we fail to set up second image, it's not a given that we
1132 * can safely root off the first.
1133 */
1134 error = imageboot_mount_image(root_path, 1, type);
1135 if (error) {
1136 panic("Failed on second stage of imageboot.");
1137 }
1138
1139 done:
1140 zfree(ZV_NAMEI, root_path);
1141
1142 DBG_TRACE("%s: exit\n", __FUNCTION__);
1143
1144 return;
1145 }