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