]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/imageboot.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / bsd / kern / imageboot.c
1 /*
2 * Copyright (c) 2006 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/filedesc.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/imageboot.h>
38 #include <kern/assert.h>
39
40 #include <sys/namei.h>
41 #include <sys/fcntl.h>
42 #include <sys/vnode.h>
43 #include <sys/sysproto.h>
44 #include <sys/csr.h>
45 #include <miscfs/devfs/devfsdefs.h>
46 #include <libkern/crypto/sha2.h>
47 #include <libkern/crypto/rsa.h>
48 #include <libkern/OSKextLibPrivate.h>
49
50 #include <kern/kalloc.h>
51
52 #include <pexpert/pexpert.h>
53 #include <kern/chunklist.h>
54
55 extern struct filedesc filedesc0;
56
57 extern int (*mountroot)(void);
58 extern char rootdevice[DEVMAXNAMESIZE];
59
60 #define DEBUG_IMAGEBOOT 0
61
62 #if DEBUG_IMAGEBOOT
63 #define DBG_TRACE(...) printf(__VA_ARGS__)
64 #else
65 #define DBG_TRACE(...) do {} while(0)
66 #endif
67
68 extern int di_root_image(const char *path, char *devname, size_t devsz, dev_t *dev_p);
69 extern int di_root_ramfile_buf(void *buf, size_t bufsz, char *devname, size_t devsz, dev_t *dev_p);
70
71 static boolean_t imageboot_setup_new(void);
72
73 #define kIBFilePrefix "file://"
74
75 __private_extern__ int
76 imageboot_format_is_valid(const char *root_path)
77 {
78 return (strncmp(root_path, kIBFilePrefix,
79 strlen(kIBFilePrefix)) == 0);
80 }
81
82 static void
83 vnode_get_and_drop_always(vnode_t vp)
84 {
85 vnode_getalways(vp);
86 vnode_rele(vp);
87 vnode_put(vp);
88 }
89
90 __private_extern__ int
91 imageboot_needed(void)
92 {
93 int result = 0;
94 char *root_path = NULL;
95
96 DBG_TRACE("%s: checking for presence of root path\n", __FUNCTION__);
97
98 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
99 if (root_path == NULL)
100 panic("%s: M_NAMEI zone exhausted", __FUNCTION__);
101
102 /* Check for first layer */
103 if (!(PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) ||
104 PE_parse_boot_argn("rp", root_path, MAXPATHLEN) ||
105 PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) ||
106 PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN))) {
107 goto out;
108 }
109
110 /* Sanity-check first layer */
111 if (imageboot_format_is_valid(root_path)) {
112 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
113 } else {
114 goto out;
115 }
116
117 result = 1;
118
119 /* Check for second layer */
120 if (!(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) ||
121 PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN))) {
122 goto out;
123 }
124
125 /* Sanity-check second layer */
126 if (imageboot_format_is_valid(root_path)) {
127 DBG_TRACE("%s: Found %s\n", __FUNCTION__, root_path);
128 } else {
129 panic("%s: Invalid URL scheme for %s\n",
130 __FUNCTION__, root_path);
131 }
132
133 out:
134 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
135
136 return (result);
137 }
138
139
140 /*
141 * Swaps in new root filesystem based on image path.
142 * Current root filesystem is removed from mount list and
143 * tagged MNTK_BACKS_ROOT, MNT_ROOTFS is cleared on it, and
144 * "rootvnode" is reset. Root vnode of currentroot filesystem
145 * is returned with usecount (no iocount).
146 */
147 __private_extern__ int
148 imageboot_mount_image(const char *root_path, int height)
149 {
150 dev_t dev;
151 int error;
152 vnode_t old_rootvnode = NULL;
153 vnode_t newdp;
154 mount_t new_rootfs;
155
156 error = di_root_image(root_path, rootdevice, DEVMAXNAMESIZE, &dev);
157 if (error) {
158 panic("%s: di_root_image failed: %d\n", __FUNCTION__, error);
159 }
160
161 rootdev = dev;
162 mountroot = NULL;
163 printf("%s: root device 0x%x\n", __FUNCTION__, rootdev);
164 error = vfs_mountroot();
165 if (error != 0) {
166 panic("vfs_mountroot() failed.\n");
167 }
168
169 /*
170 * Get the vnode for '/'.
171 * Set fdp->fd_fd.fd_cdir to reference it.
172 */
173 if (VFS_ROOT(TAILQ_LAST(&mountlist,mntlist), &newdp, vfs_context_kernel()))
174 panic("%s: cannot find root vnode", __FUNCTION__);
175
176 if (rootvnode != NULL) {
177 /* remember the old rootvnode, but remove it from mountlist */
178 mount_t old_rootfs;
179
180 old_rootvnode = rootvnode;
181 old_rootfs = rootvnode->v_mount;
182
183 mount_list_remove(old_rootfs);
184
185 mount_lock(old_rootfs);
186 #ifdef CONFIG_IMGSRC_ACCESS
187 old_rootfs->mnt_kern_flag |= MNTK_BACKS_ROOT;
188 #endif /* CONFIG_IMGSRC_ACCESS */
189 old_rootfs->mnt_flag &= ~MNT_ROOTFS;
190 mount_unlock(old_rootfs);
191 }
192
193 /* switch to the new rootvnode */
194 rootvnode = newdp;
195
196 new_rootfs = rootvnode->v_mount;
197 mount_lock(new_rootfs);
198 new_rootfs->mnt_flag |= MNT_ROOTFS;
199 mount_unlock(new_rootfs);
200
201 vnode_ref(newdp);
202 vnode_put(newdp);
203 filedesc0.fd_cdir = newdp;
204 DBG_TRACE("%s: root switched\n", __FUNCTION__);
205
206 if (old_rootvnode != NULL) {
207 #ifdef CONFIG_IMGSRC_ACCESS
208 if (height >= 0 && PE_imgsrc_mount_supported()) {
209 imgsrc_rootvnodes[height] = old_rootvnode;
210 } else {
211 vnode_get_and_drop_always(old_rootvnode);
212 }
213 #else
214 height = 0; /* keep the compiler from complaining */
215 vnode_get_and_drop_always(old_rootvnode);
216 #endif /* CONFIG_IMGSRC_ACCESS */
217 }
218 return 0;
219 }
220
221
222 /*
223 * Authenticated root-dmg support
224 */
225
226 #define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
227 #define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
228
229 #define kfree_safe(x) do { if ((x)) { kfree_addr((x)); (x) = NULL; } } while (0)
230
231 enum {
232 MISSING_SIG = -1,
233 INVALID_SIG = -2
234 };
235
236 static void
237 key_byteswap(void *_dst, const void *_src, size_t len)
238 {
239 uint32_t *dst __attribute__((align_value(1))) = _dst;
240 const uint32_t *src __attribute__((align_value(1))) = _src;
241
242 assert(len % sizeof(uint32_t) == 0);
243
244 len = len / sizeof(uint32_t);
245 for (size_t i = 0; i < len; i++) {
246 dst[len-i-1] = OSSwapInt32(src[i]);
247 }
248 }
249
250 static int
251 read_file(const char *path, void **bufp, size_t *bufszp)
252 {
253 int err = 0;
254 struct nameidata ndp = {};
255 struct vnode *vp = NULL;
256 off_t fsize = 0;
257 int resid = 0;
258 char *buf = NULL;
259 bool doclose = false;
260
261 vfs_context_t ctx = vfs_context_kernel();
262 proc_t p = vfs_context_proc(ctx);
263 kauth_cred_t kerncred = vfs_context_ucred(ctx);
264
265 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(path), ctx);
266 if ((err = namei(&ndp)) != 0) {
267 AUTHPRNT("namei failed (%s)", path);
268 goto out;
269 }
270 nameidone(&ndp);
271 vp = ndp.ni_vp;
272
273 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
274 AUTHPRNT("failed to get vnode size");
275 goto out;
276 }
277 if (fsize < 0) {
278 panic("negative file size");
279 }
280
281 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
282 AUTHPRNT("failed to open vnode");
283 goto out;
284 }
285 doclose = true;
286
287 /* if bufsz is non-zero, cap the read at bufsz bytes */
288 if (*bufszp && *bufszp < (size_t)fsize) {
289 fsize = *bufszp;
290 }
291
292 buf = kalloc(fsize);
293 if (buf == NULL) {
294 err = ENOMEM;
295 goto out;
296 }
297
298 if ((err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, fsize, 0, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p)) != 0) {
299 AUTHPRNT("vn_rdwr() failed");
300 goto out;
301 }
302
303 if (resid) {
304 /* didnt get everything we wanted */
305 AUTHPRNT("vn_rdwr resid = %d", resid);
306 err = EINVAL;
307 goto out;
308 }
309
310 out:
311 if (doclose) {
312 VNOP_CLOSE(vp, FREAD, ctx);
313 }
314 if (vp) {
315 vnode_put(vp);
316 vp = NULL;
317 }
318
319 if (err) {
320 kfree_safe(buf);
321 } else {
322 *bufp = buf;
323 *bufszp = fsize;
324 }
325
326 return err;
327 }
328
329 static int
330 validate_signature(const uint8_t *key_msb, size_t keylen, uint8_t *sig_msb, size_t siglen, uint8_t *digest)
331 {
332 int err = 0;
333 bool sig_valid = false;
334 uint8_t *sig = NULL;
335
336 const uint8_t exponent[] = { 0x01, 0x00, 0x01 };
337 uint8_t *modulus = kalloc(keylen);
338 rsa_pub_ctx *rsa_ctx = kalloc(sizeof(rsa_pub_ctx));
339 sig = kalloc(siglen);
340
341 if (modulus == NULL || rsa_ctx == NULL || sig == NULL) {
342 err = ENOMEM;
343 goto out;
344 }
345
346 bzero(rsa_ctx, sizeof(rsa_pub_ctx));
347 key_byteswap(modulus, key_msb, keylen);
348 key_byteswap(sig, sig_msb, siglen);
349
350 err = rsa_make_pub(rsa_ctx,
351 sizeof(exponent), exponent,
352 CHUNKLIST_PUBKEY_LEN, modulus);
353 if (err) {
354 AUTHPRNT("rsa_make_pub() failed");
355 goto out;
356 }
357
358 err = rsa_verify_pkcs1v15(rsa_ctx, CC_DIGEST_OID_SHA256,
359 SHA256_DIGEST_LENGTH, digest,
360 siglen, sig,
361 &sig_valid);
362 if (err) {
363 sig_valid = false;
364 AUTHPRNT("rsa_verify() failed");
365 err = EINVAL;
366 goto out;
367 }
368
369 out:
370 kfree_safe(sig);
371 kfree_safe(rsa_ctx);
372 kfree_safe(modulus);
373
374 if (err) {
375 return err;
376 } else if (sig_valid == true) {
377 return 0; /* success */
378 } else {
379 return INVALID_SIG;
380 }
381 }
382
383 static int
384 validate_chunklist(void *buf, size_t len)
385 {
386 int err = 0;
387 size_t sigsz = 0;
388 size_t sig_end = 0;
389 size_t chunks_end = 0;
390 bool valid_sig = false;
391 struct chunklist_hdr *hdr = buf;
392
393 if (len < sizeof(struct chunklist_hdr)) {
394 AUTHPRNT("no space for header");
395 return EINVAL;
396 }
397
398 /* recognized file format? */
399 if (hdr->cl_magic != CHUNKLIST_MAGIC ||
400 hdr->cl_file_ver != CHUNKLIST_FILE_VERSION_10 ||
401 hdr->cl_chunk_method != CHUNKLIST_SIGNATURE_METHOD_10 ||
402 hdr->cl_sig_method != CHUNKLIST_SIGNATURE_METHOD_10) {
403 AUTHPRNT("unrecognized chunklist format");
404 return EINVAL;
405 }
406
407 /* does the chunk list fall within the bounds of the buffer? */
408 if (os_mul_and_add_overflow(hdr->cl_chunk_count, sizeof(struct chunklist_chunk), hdr->cl_chunk_offset, &chunks_end) ||
409 hdr->cl_chunk_offset < sizeof(struct chunklist_hdr) || chunks_end > len) {
410 AUTHPRNT("invalid chunk_count (%llu) or chunk_offset (%llu)",
411 hdr->cl_chunk_count, hdr->cl_chunk_offset);
412 return EINVAL;
413 }
414
415 /* does the signature fall within the bounds of the buffer? */
416 if (os_add_overflow(hdr->cl_sig_offset, sizeof(struct chunklist_sig), &sig_end) ||
417 hdr->cl_sig_offset < sizeof(struct chunklist_hdr) ||
418 hdr->cl_sig_offset < chunks_end ||
419 hdr->cl_sig_offset > len) {
420 AUTHPRNT("invalid signature offset (%llu)", hdr->cl_sig_offset);
421 return EINVAL;
422 }
423
424 if (sig_end > len || os_sub_overflow(len, hdr->cl_sig_offset, &sigsz) || sigsz != CHUNKLIST_SIG_LEN) {
425 /* missing or incorrect signature size */
426 return MISSING_SIG;
427 }
428
429 AUTHDBG("hashing chunklist");
430
431 /* hash the chunklist (excluding the signature) */
432 uint8_t sha_digest[SHA256_DIGEST_LENGTH];
433 SHA256_CTX sha_ctx;
434 SHA256_Init(&sha_ctx);
435 SHA256_Update(&sha_ctx, buf, hdr->cl_sig_offset);
436 SHA256_Final(sha_digest, &sha_ctx);
437
438 AUTHDBG("validating chunklist signature against pub keys");
439 for (size_t i = 0; i < CHUNKLIST_NPUBKEYS; i++) {
440 const struct chunklist_pubkey *key = &chunklist_pubkeys[i];
441 err = validate_signature(key->key, CHUNKLIST_PUBKEY_LEN,
442 buf + hdr->cl_sig_offset, sigsz, sha_digest);
443 if (err == 0) {
444 AUTHDBG("validated chunklist signature with key %lu (prod=%d)", i, key->isprod);
445 valid_sig = key->isprod;
446 #if IMAGEBOOT_ALLOW_DEVKEYS
447 if (!key->isprod) {
448 /* allow dev keys in dev builds only */
449 AUTHDBG("*** allowing DEV key: this will fail in customer builds ***");
450 valid_sig = true;
451 }
452 #endif
453 goto out;
454 } else if (err == INVALID_SIG) {
455 /* try the next key */
456 } else {
457 goto out; /* something bad happened */
458 }
459 }
460
461 /* At this point we tried all the keys: nothing went wrong but none of them
462 * signed our chunklist. */
463 AUTHPRNT("signature did not verify against any known public key");
464
465 out:
466 if (err) {
467 return err;
468 } else if (valid_sig == true) {
469 return 0; /* signed, and everything checked out */
470 } else {
471 return EINVAL;
472 }
473 }
474
475 static int
476 validate_root_image(const char *root_path, void *chunklist)
477 {
478 int err = 0;
479 struct chunklist_hdr *hdr = chunklist;
480 struct chunklist_chunk *chk = NULL;
481 size_t ch = 0;
482 struct nameidata ndp = {};
483 struct vnode *vp = NULL;
484 off_t fsize = 0;
485 off_t offset = 0;
486 bool doclose = false;
487 size_t bufsz = 0;
488 void *buf = NULL;
489
490 vfs_context_t ctx = vfs_context_kernel();
491 kauth_cred_t kerncred = vfs_context_ucred(ctx);
492 proc_t p = vfs_context_proc(ctx);
493
494 AUTHDBG("validating root dmg %s", root_path);
495
496 /*
497 * Open the DMG
498 */
499 NDINIT(&ndp, LOOKUP, OP_OPEN, LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(root_path), ctx);
500 if ((err = namei(&ndp)) != 0) {
501 AUTHPRNT("namei failed (%s)", root_path);
502 goto out;
503 }
504 nameidone(&ndp);
505 vp = ndp.ni_vp;
506
507 if (vp->v_type != VREG) {
508 err = EINVAL;
509 goto out;
510 }
511
512 if ((err = vnode_size(vp, &fsize, ctx)) != 0) {
513 AUTHPRNT("failed to get vnode size");
514 goto out;
515 }
516
517 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
518 AUTHPRNT("failed to open vnode");
519 goto out;
520 }
521 doclose = true;
522
523 /*
524 * Iterate the chunk list and check each chunk
525 */
526 chk = chunklist + hdr->cl_chunk_offset;
527 for (ch = 0; ch < hdr->cl_chunk_count; ch++) {
528 int resid = 0;
529
530 if (!buf) {
531 /* allocate buffer based on first chunk size */
532 buf = kalloc(chk->chunk_size);
533 if (buf == NULL) {
534 err = ENOMEM;
535 goto out;
536 }
537 bufsz = chk->chunk_size;
538 }
539
540 if (chk->chunk_size > bufsz) {
541 AUTHPRNT("chunk size too big");
542 err = EINVAL;
543 goto out;
544 }
545
546 err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, chk->chunk_size, offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p);
547 if (err) {
548 AUTHPRNT("vn_rdrw fail (err = %d, resid = %d)", err, resid);
549 goto out;
550 }
551 if (resid) {
552 err = EINVAL;
553 AUTHPRNT("chunk covered non-existant part of image");
554 goto out;
555 }
556
557 /* calculate the SHA256 of this chunk */
558 uint8_t sha_digest[SHA256_DIGEST_LENGTH];
559 SHA256_CTX sha_ctx;
560 SHA256_Init(&sha_ctx);
561 SHA256_Update(&sha_ctx, buf, chk->chunk_size);
562 SHA256_Final(sha_digest, &sha_ctx);
563
564 /* Check the calculated SHA matches the chunk list */
565 if (bcmp(sha_digest, chk->chunk_sha256, SHA256_DIGEST_LENGTH) != 0) {
566 AUTHPRNT("SHA mismatch on chunk %lu (offset %lld, size %u)", ch, offset, chk->chunk_size);
567 err = EINVAL;
568 goto out;
569 }
570
571 if (os_add_overflow(offset, chk->chunk_size, &offset)) {
572 err = EINVAL;
573 goto out;
574 }
575 chk++;
576 }
577
578 if (offset != fsize) {
579 AUTHPRNT("chunklist did not cover entire file (offset = %lld, fsize = %lld)", offset, fsize);
580 err = EINVAL;
581 goto out;
582 }
583
584 out:
585 kfree_safe(buf);
586 if (doclose) {
587 VNOP_CLOSE(vp, FREAD, ctx);
588 }
589 if (vp) {
590 vnode_put(vp);
591 vp = NULL;
592 }
593
594 return err;
595 }
596
597 static int
598 construct_chunklist_path(const char *root_path, char **bufp)
599 {
600 int err = 0;
601 char *path = NULL;
602 size_t len = 0;
603
604 path = kalloc(MAXPATHLEN);
605 if (path == NULL) {
606 AUTHPRNT("failed to allocate space for chunklist path");
607 err = ENOMEM;
608 goto out;
609 }
610
611 len = strnlen(root_path, MAXPATHLEN);
612 if (len < MAXPATHLEN && len > strlen(".dmg")) {
613 /* correctly terminated string with space for extension */
614 } else {
615 AUTHPRNT("malformed root path");
616 err = EINVAL;
617 goto out;
618 }
619
620 len = strlcpy(path, root_path, MAXPATHLEN);
621 if (len >= MAXPATHLEN) {
622 AUTHPRNT("root path is too long");
623 err = EINVAL;
624 goto out;
625 }
626
627 path[len - strlen(".dmg")] = '\0';
628 len = strlcat(path, ".chunklist", MAXPATHLEN);
629 if (len >= MAXPATHLEN) {
630 AUTHPRNT("chunklist path is too long");
631 err = EINVAL;
632 goto out;
633 }
634
635 out:
636 if (err) {
637 kfree_safe(path);
638 } else {
639 *bufp = path;
640 }
641 return err;
642 }
643
644 static int
645 authenticate_root(const char *root_path)
646 {
647 char *chunklist_path = NULL;
648 void *chunklist_buf = NULL;
649 size_t chunklist_len = 32*1024*1024UL;
650 int err = 0;
651
652 err = construct_chunklist_path(root_path, &chunklist_path);
653 if (err) {
654 AUTHPRNT("failed creating chunklist path");
655 goto out;
656 }
657
658 AUTHDBG("validating root against chunklist %s", chunklist_path);
659
660 /*
661 * Read and authenticate the chunklist, then validate the root image against
662 * the chunklist.
663 */
664
665 AUTHDBG("reading chunklist");
666 err = read_file(chunklist_path, &chunklist_buf, &chunklist_len);
667 if (err) {
668 AUTHPRNT("failed to read chunklist");
669 goto out;
670 }
671
672 AUTHDBG("validating chunklist");
673 err = validate_chunklist(chunklist_buf, chunklist_len);
674 if (err < 0) {
675 AUTHDBG("missing or incorrect signature on chunklist");
676 goto out;
677 } else if (err) {
678 AUTHPRNT("failed to validate chunklist");
679 goto out;
680 } else {
681 AUTHDBG("successfully validated chunklist");
682 }
683
684 AUTHDBG("validating root image against chunklist");
685 err = validate_root_image(root_path, chunklist_buf);
686 if (err) {
687 AUTHPRNT("failed to validate root image against chunklist (%d)", err);
688 goto out;
689 }
690
691 /* everything checked out - go ahead and mount this */
692 AUTHDBG("root image authenticated");
693
694 out:
695 kfree_safe(chunklist_buf);
696 kfree_safe(chunklist_path);
697 return err;
698 }
699
700 static const uuid_t *
701 getuuidfromheader_safe(const void *buf, size_t bufsz, size_t *uuidsz)
702 {
703 const struct uuid_command *cmd = NULL;
704 const kernel_mach_header_t *mh = buf;
705
706 /* space for the header and at least one load command? */
707 if (bufsz < sizeof(kernel_mach_header_t) + sizeof(struct uuid_command)) {
708 AUTHPRNT("libkern image too small");
709 return NULL;
710 }
711
712 /* validate the mach header */
713 if (mh->magic != MH_MAGIC_64 || (mh->sizeofcmds > bufsz - sizeof(kernel_mach_header_t))) {
714 AUTHPRNT("invalid MachO header");
715 return NULL;
716 }
717
718 /* iterate the load commands */
719 size_t offset = sizeof(kernel_mach_header_t);
720 for (size_t i = 0; i < mh->ncmds; i++) {
721 cmd = buf + offset;
722
723 if (cmd->cmd == LC_UUID) {
724 *uuidsz = sizeof(cmd->uuid);
725 return &cmd->uuid;
726 }
727
728 if (os_add_overflow(cmd->cmdsize, offset, &offset) ||
729 offset > bufsz - sizeof(struct uuid_command)) {
730 return NULL;
731 }
732 }
733
734 return NULL;
735 }
736
737 static const char *libkern_path = "/System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern";
738 static const char *libkern_bundle = "com.apple.kpi.libkern";
739
740 /*
741 * Check that the UUID of the libkern currently loaded matches the one on disk.
742 */
743 static int
744 auth_version_check(void)
745 {
746 int err = 0;
747 void *buf = NULL;
748 size_t bufsz = 4*1024*1024UL;
749
750 /* get the UUID of the libkern in /S/L/E */
751
752 err = read_file(libkern_path, &buf, &bufsz);
753 if (err) {
754 goto out;
755 }
756
757 unsigned long uuidsz = 0;
758 const uuid_t *img_uuid = getuuidfromheader_safe(buf, bufsz, &uuidsz);
759 if (img_uuid == NULL || uuidsz != sizeof(uuid_t)) {
760 AUTHPRNT("invalid UUID (sz = %lu)", uuidsz);
761 err = EINVAL;
762 goto out;
763 }
764
765 /* Get the UUID of the loaded libkern */
766 uuid_t live_uuid;
767 err = OSKextGetUUIDForName(libkern_bundle, live_uuid);
768 if (err) {
769 AUTHPRNT("could not find loaded libkern");
770 goto out;
771 }
772
773 /* ... and compare them */
774 if (bcmp(live_uuid, img_uuid, uuidsz) != 0) {
775 AUTHPRNT("UUID of running libkern does not match %s", libkern_path);
776
777 uuid_string_t img_uuid_str, live_uuid_str;
778 uuid_unparse(*img_uuid, img_uuid_str);
779 uuid_unparse(live_uuid, live_uuid_str);
780 AUTHPRNT("loaded libkern UUID = %s", live_uuid_str);
781 AUTHPRNT("on-disk libkern UUID = %s", img_uuid_str);
782
783 err = EINVAL;
784 goto out;
785 }
786
787 /* UUID matches! */
788
789 out:
790 kfree_safe(buf);
791 return err;
792 }
793
794 #if 0
795 int
796 auth_imgboot_test(proc_t __unused ap, struct auth_imgboot_test_args *uap, int32_t *retval)
797 {
798 int ret = 0;
799 int err;
800 char path[MAXPATHLEN];
801 vm_size_t len;
802 *retval = 0;
803
804 err = copyinstr(uap->path, path, MAXPATHLEN, &len);
805 if (err) {
806 return err;
807 }
808 if (len >= MAXPATHLEN) {
809 return ENAMETOOLONG;
810 }
811
812 AUTHDBG("authenticating root image at %s", path);
813 err = authenticate_root(path);
814 if (err) {
815 AUTHPRNT("root authentication FAIL (%d)", err);
816 ret = err;
817 } else {
818 AUTHDBG("successfully authenticated %s", path);
819 }
820
821 AUTHDBG("checking root image version");
822 err = auth_version_check();
823 if (err) {
824 AUTHPRNT("root image version check FAIL (%d)", err);
825 err = err ?: ret;
826 } else {
827 AUTHPRNT("root version check success (%d)", err);
828 }
829
830 if (ret < 0) {
831 return EINVAL; /* negative return values have special meaning */
832 }
833 return ret;
834 }
835 #endif
836
837 /*
838 * Attach the image at 'path' as a ramdisk and mount it as our new rootfs.
839 * All existing mounts are first umounted.
840 */
841 static int
842 imageboot_mount_ramdisk(const char *path)
843 {
844 int err = 0;
845 size_t bufsz = 0;
846 void *buf = NULL;
847 dev_t dev;
848 vnode_t newdp;
849 mount_t new_rootfs;
850
851 /* Read our target image from disk */
852 err = read_file(path, &buf, &bufsz);
853 if (err) {
854 printf("%s: failed: read_file() = %d\n", __func__, err);
855 goto out;
856 }
857 DBG_TRACE("%s: read '%s' sz = %lu\n", __func__, path, bufsz);
858
859 #if CONFIG_IMGSRC_ACCESS
860 /* Re-add all root mounts to the mount list in the correct order... */
861 mount_list_remove(rootvnode->v_mount);
862 for (int i = 0; i < MAX_IMAGEBOOT_NESTING; i++) {
863 struct vnode *vn = imgsrc_rootvnodes[i];
864 if (vn) {
865 vnode_getalways(vn);
866 imgsrc_rootvnodes[i] = NULLVP;
867
868 mount_t mnt = vn->v_mount;
869 mount_lock(mnt);
870 mnt->mnt_flag |= MNT_ROOTFS;
871 mount_list_add(mnt);
872 mount_unlock(mnt);
873
874 vnode_rele(vn);
875 vnode_put(vn);
876 }
877 }
878 mount_list_add(rootvnode->v_mount);
879 #endif
880
881 /* ... and unmount everything */
882 vnode_get_and_drop_always(rootvnode);
883 filedesc0.fd_cdir = NULL;
884 rootvnode = NULL;
885 vfs_unmountall();
886
887 /* Attach the ramfs image ... */
888 err = di_root_ramfile_buf(buf, bufsz, rootdevice, DEVMAXNAMESIZE, &dev);
889 if (err) {
890 printf("%s: failed: di_root_ramfile_buf() = %d\n", __func__, err);
891 goto out;
892 }
893
894 /* ... and mount it */
895 rootdev = dev;
896 mountroot = NULL;
897 err = vfs_mountroot();
898 if (err) {
899 printf("%s: failed: vfs_mountroot() = %d\n", __func__, err);
900 goto out;
901 }
902
903 /* Switch to new root vnode */
904 if (VFS_ROOT(TAILQ_LAST(&mountlist,mntlist), &newdp, vfs_context_kernel())) {
905 panic("%s: cannot find root vnode", __func__);
906 }
907 rootvnode = newdp;
908 rootvnode->v_flag |= VROOT;
909 new_rootfs = rootvnode->v_mount;
910 mount_lock(new_rootfs);
911 new_rootfs->mnt_flag |= MNT_ROOTFS;
912 mount_unlock(new_rootfs);
913
914 vnode_ref(newdp);
915 vnode_put(newdp);
916 filedesc0.fd_cdir = newdp;
917
918 DBG_TRACE("%s: root switched\n", __func__);
919
920 out:
921 if (err) {
922 kfree_safe(buf);
923 }
924 return err;
925 }
926
927 static boolean_t
928 imageboot_setup_new()
929 {
930 int error;
931 char *root_path = NULL;
932 int height = 0;
933 boolean_t done = FALSE;
934 boolean_t auth_root = FALSE;
935 boolean_t ramdisk_root = FALSE;
936
937 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
938 assert(root_path != NULL);
939
940 unsigned imgboot_arg;
941 if (PE_parse_boot_argn("-rootdmg-ramdisk", &imgboot_arg, sizeof(imgboot_arg))) {
942 ramdisk_root = TRUE;
943 }
944
945 if (PE_parse_boot_argn(IMAGEBOOT_CONTAINER_ARG, root_path, MAXPATHLEN) == TRUE) {
946 printf("%s: container image url is %s\n", __FUNCTION__, root_path);
947 error = imageboot_mount_image(root_path, height);
948 if (error != 0) {
949 panic("Failed to mount container image.");
950 }
951
952 height++;
953 }
954
955 if (PE_parse_boot_argn(IMAGEBOOT_AUTHROOT_ARG, root_path, MAXPATHLEN) == TRUE) {
956 auth_root = TRUE;
957 } else if (PE_parse_boot_argn(IMAGEBOOT_ROOT_ARG, root_path, MAXPATHLEN) == FALSE) {
958 if (height > 0) {
959 panic("%s specified without %s?\n", IMAGEBOOT_CONTAINER_ARG, IMAGEBOOT_ROOT_ARG);
960 }
961 goto out;
962 }
963
964 printf("%s: root image url is %s\n", __func__, root_path);
965
966 #if CONFIG_CSR
967 if (auth_root && (csr_check(CSR_ALLOW_ANY_RECOVERY_OS) == 0)) {
968 AUTHPRNT("CSR_ALLOW_ANY_RECOVERY_OS set, skipping root image authentication");
969 auth_root = false;
970 }
971 #endif
972
973 /* Make a copy of the path to URL-decode */
974 char *path_alloc = kalloc(MAXPATHLEN);
975 if (path_alloc == NULL) {
976 panic("imageboot path allocation failed\n");
977 }
978 char *path = path_alloc;
979
980 size_t len = strlen(kIBFilePrefix);
981 strlcpy(path, root_path, MAXPATHLEN);
982 if (strncmp(kIBFilePrefix, path, len) == 0) {
983 /* its a URL - remove the file:// prefix and percent-decode */
984 path += len;
985 url_decode(path);
986 }
987
988 if (auth_root) {
989 AUTHDBG("authenticating root image at %s", path);
990 error = authenticate_root(path);
991 if (error) {
992 panic("root image authentication failed (err = %d)\n", error);
993 }
994 AUTHDBG("successfully authenticated %s", path);
995 }
996
997 if (ramdisk_root) {
998 error = imageboot_mount_ramdisk(path);
999 } else {
1000 error = imageboot_mount_image(root_path, height);
1001 }
1002
1003 kfree_safe(path_alloc);
1004
1005 if (error) {
1006 panic("Failed to mount root image (err=%d, auth=%d, ramdisk=%d)\n",
1007 error, auth_root, ramdisk_root);
1008 }
1009
1010 if (auth_root) {
1011 /* check that the image version matches the running kernel */
1012 AUTHDBG("checking root image version");
1013 error = auth_version_check();
1014 if (error) {
1015 panic("root image version check failed");
1016 } else {
1017 AUTHDBG("root image version matches kernel");
1018 }
1019 }
1020
1021 done = TRUE;
1022
1023 out:
1024 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
1025 return done;
1026 }
1027
1028 __private_extern__ void
1029 imageboot_setup()
1030 {
1031 int error = 0;
1032 char *root_path = NULL;
1033
1034 DBG_TRACE("%s: entry\n", __FUNCTION__);
1035
1036 if (rootvnode == NULL) {
1037 panic("imageboot_setup: rootvnode is NULL.");
1038 }
1039
1040 /*
1041 * New boot-arg scheme:
1042 * root-dmg : the dmg that will be the root filesystem.
1043 * auth-root-dmg : same as root-dmg but with image authentication.
1044 * container-dmg : an optional dmg that contains the root-dmg.
1045 */
1046 if (imageboot_setup_new()) {
1047 return;
1048 }
1049
1050 MALLOC_ZONE(root_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK);
1051 assert(root_path != NULL);
1052
1053 /*
1054 * Look for outermost disk image to root from. If we're doing a nested boot,
1055 * there's some sense in which the outer image never needs to be the root filesystem,
1056 * but it does need very similar treatment: it must not be unmounted, needs a fake
1057 * device vnode created for it, and should not show up in getfsstat() until exposed
1058 * with MNT_IMGSRC. We just make it the temporary root.
1059 */
1060 if((PE_parse_boot_argn("rp", root_path, MAXPATHLEN) == FALSE) &&
1061 (PE_parse_boot_argn("rp0", root_path, MAXPATHLEN) == FALSE)) {
1062 panic("%s: no valid path to image.\n", __FUNCTION__);
1063 }
1064
1065 printf("%s: root image url is %s\n", __FUNCTION__, root_path);
1066
1067 error = imageboot_mount_image(root_path, 0);
1068 if (error) {
1069 panic("Failed on first stage of imageboot.");
1070 }
1071
1072 /*
1073 * See if we are rooting from a nested image
1074 */
1075 if(PE_parse_boot_argn("rp1", root_path, MAXPATHLEN) == FALSE) {
1076 goto done;
1077 }
1078
1079 printf("%s: second level root image url is %s\n", __FUNCTION__, root_path);
1080
1081 /*
1082 * If we fail to set up second image, it's not a given that we
1083 * can safely root off the first.
1084 */
1085 error = imageboot_mount_image(root_path, 1);
1086 if (error) {
1087 panic("Failed on second stage of imageboot.");
1088 }
1089
1090 done:
1091 FREE_ZONE(root_path, MAXPATHLEN, M_NAMEI);
1092
1093 DBG_TRACE("%s: exit\n", __FUNCTION__);
1094
1095 return;
1096 }