]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/chunklist.c
be51d58c16015f024d2435f05a96ce7afa615fa5
[apple/xnu.git] / bsd / kern / chunklist.c
1 /*
2 * Copyright (c) 2019-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/filedesc.h>
36 #include <sys/vnode_internal.h>
37 #include <sys/imageboot.h>
38 #include <kern/assert.h>
39 #include <kern/mach_fat.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
51 #include <kern/chunklist.h>
52 #include <kern/kalloc.h>
53
54 #include <pexpert/pexpert.h>
55
56 #define AUTHDBG(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
57 #define AUTHPRNT(fmt, args...) do { printf("%s: " fmt "\n", __func__, ##args); } while (0)
58 #define kheap_free_safe(h, x, l) do { if ((x)) { kheap_free(h, x, l); (x) = NULL; } } while (0)
59
60 static const char *libkern_path = "/System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern";
61 static const char *libkern_bundle = "com.apple.kpi.libkern";
62
63 extern boolean_t kernelcache_uuid_valid;
64 extern uuid_t kernelcache_uuid;
65
66 #if DEBUG
67 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc.debug";
68 #elif KASAN
69 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc.kasan";
70 #elif DEVELOPMENT
71 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc.development";
72 #else
73 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc";
74 #endif
75
76 /*
77 * Rev1 chunklist handling
78 */
79 const struct chunklist_pubkey rev1_chunklist_pubkeys[] = {
80 };
81 const size_t rev1_chunklist_num_pubkeys = sizeof(rev1_chunklist_pubkeys) / sizeof(rev1_chunklist_pubkeys[0]);
82
83 static void
84 key_byteswap(void *_dst, const void *_src, size_t len)
85 {
86 uint32_t *dst __attribute__((align_value(1))) = _dst;
87 const uint32_t *src __attribute__((align_value(1))) = _src;
88
89 assert(len % sizeof(uint32_t) == 0);
90
91 len = len / sizeof(uint32_t);
92 for (size_t i = 0; i < len; i++) {
93 dst[len - i - 1] = OSSwapInt32(src[i]);
94 }
95 }
96
97 static int
98 construct_chunklist_path(char path[static MAXPATHLEN], const char *root_path)
99 {
100 size_t len = 0;
101
102 len = strnlen(root_path, MAXPATHLEN);
103 if (len < MAXPATHLEN && len > strlen(".dmg")) {
104 /* correctly terminated string with space for extension */
105 } else {
106 AUTHPRNT("malformed root path");
107 return EOVERFLOW;
108 }
109
110 len = strlcpy(path, root_path, MAXPATHLEN);
111 if (len >= MAXPATHLEN) {
112 AUTHPRNT("root path is too long");
113 return EOVERFLOW;
114 }
115
116 path[len - strlen(".dmg")] = '\0';
117 len = strlcat(path, ".chunklist", MAXPATHLEN);
118 if (len >= MAXPATHLEN) {
119 AUTHPRNT("chunklist path is too long");
120 return EOVERFLOW;
121 }
122
123 return 0;
124 }
125
126 static int
127 validate_signature(const uint8_t *key_msb, size_t keylen, uint8_t *sig_msb, size_t siglen, uint8_t *digest)
128 {
129 int err = 0;
130 bool sig_valid = false;
131 uint8_t *sig = NULL;
132
133 const uint8_t exponent[] = { 0x01, 0x00, 0x01 };
134 rsa_pub_ctx *rsa_ctx;
135 uint8_t *modulus;
136
137
138 modulus = kheap_alloc(KHEAP_TEMP, keylen, Z_WAITOK | Z_ZERO);
139 rsa_ctx = kheap_alloc(KHEAP_TEMP, sizeof(rsa_pub_ctx),
140 Z_WAITOK | Z_ZERO);
141 sig = kheap_alloc(KHEAP_TEMP, siglen, Z_WAITOK | Z_ZERO);
142
143 if (modulus == NULL || rsa_ctx == NULL || sig == NULL) {
144 err = ENOMEM;
145 goto out;
146 }
147
148 key_byteswap(modulus, key_msb, keylen);
149 key_byteswap(sig, sig_msb, siglen);
150
151 err = rsa_make_pub(rsa_ctx,
152 sizeof(exponent), exponent,
153 CHUNKLIST_PUBKEY_LEN, modulus);
154 if (err) {
155 AUTHPRNT("rsa_make_pub() failed");
156 goto out;
157 }
158
159 err = rsa_verify_pkcs1v15(rsa_ctx, CC_DIGEST_OID_SHA256,
160 SHA256_DIGEST_LENGTH, digest,
161 siglen, sig,
162 &sig_valid);
163 if (err) {
164 sig_valid = false;
165 AUTHPRNT("rsa_verify() failed");
166 goto out;
167 }
168
169 out:
170 kheap_free_safe(KHEAP_TEMP, sig, siglen);
171 kheap_free_safe(KHEAP_TEMP, rsa_ctx, sizeof(*rsa_ctx));
172 kheap_free_safe(KHEAP_TEMP, modulus, keylen);
173
174 if (err) {
175 return err;
176 } else if (sig_valid == true) {
177 return 0; /* success */
178 } else {
179 return EAUTH;
180 }
181 }
182
183 static int
184 validate_root_image(const char *root_path, void *chunklist)
185 {
186 int err = 0;
187 struct chunklist_hdr *hdr = chunklist;
188 struct chunklist_chunk *chk = NULL;
189 size_t ch = 0;
190 struct vnode *vp = NULL;
191 off_t fsize = 0;
192 off_t offset = 0;
193 bool doclose = false;
194 size_t bufsz = 0;
195 void *buf = NULL;
196
197 vfs_context_t ctx = vfs_context_kernel();
198 kauth_cred_t kerncred = vfs_context_ucred(ctx);
199 proc_t p = vfs_context_proc(ctx);
200
201 AUTHDBG("validating root dmg %s", root_path);
202
203 vp = imgboot_get_image_file(root_path, &fsize, &err);
204 if (vp == NULL) {
205 goto out;
206 }
207
208 if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
209 AUTHPRNT("failed to open vnode");
210 goto out;
211 }
212 doclose = true;
213
214 /*
215 * Iterate the chunk list and check each chunk
216 */
217 chk = chunklist + hdr->cl_chunk_offset;
218 for (ch = 0; ch < hdr->cl_chunk_count; ch++) {
219 int resid = 0;
220
221 if (!buf) {
222 /* allocate buffer based on first chunk size */
223 buf = kheap_alloc(KHEAP_TEMP, chk->chunk_size, Z_WAITOK);
224 if (buf == NULL) {
225 err = ENOMEM;
226 goto out;
227 }
228 bufsz = chk->chunk_size;
229 }
230
231 if (chk->chunk_size > bufsz) {
232 AUTHPRNT("chunk size too big");
233 err = EINVAL;
234 goto out;
235 }
236
237 err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, chk->chunk_size,
238 offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p);
239 if (err) {
240 AUTHPRNT("vn_rdrw fail (err = %d, resid = %d)", err, resid);
241 goto out;
242 }
243 if (resid) {
244 err = EINVAL;
245 AUTHPRNT("chunk covered non-existant part of image");
246 goto out;
247 }
248
249 /* calculate the SHA256 of this chunk */
250 uint8_t sha_digest[SHA256_DIGEST_LENGTH];
251 SHA256_CTX sha_ctx;
252 SHA256_Init(&sha_ctx);
253 SHA256_Update(&sha_ctx, buf, chk->chunk_size);
254 SHA256_Final(sha_digest, &sha_ctx);
255
256 /* Check the calculated SHA matches the chunk list */
257 if (bcmp(sha_digest, chk->chunk_sha256, SHA256_DIGEST_LENGTH) != 0) {
258 AUTHPRNT("SHA mismatch on chunk %lu (offset %lld, size %u)", ch, offset, chk->chunk_size);
259 err = EINVAL;
260 goto out;
261 }
262
263 if (os_add_overflow(offset, chk->chunk_size, &offset)) {
264 err = EINVAL;
265 goto out;
266 }
267 chk++;
268 }
269
270 if (offset != fsize) {
271 AUTHPRNT("chunklist did not cover entire file (offset = %lld, fsize = %lld)", offset, fsize);
272 err = EINVAL;
273 goto out;
274 }
275
276 out:
277 kheap_free_safe(KHEAP_TEMP, buf, bufsz);
278 if (doclose) {
279 VNOP_CLOSE(vp, FREAD, ctx);
280 }
281 if (vp) {
282 vnode_put(vp);
283 vp = NULL;
284 }
285
286 return err;
287 }
288
289 static const uuid_t *
290 getuuidfromheader_safe(const void *buf, size_t bufsz, size_t *uuidsz)
291 {
292 const struct uuid_command *cmd = NULL;
293 const kernel_mach_header_t *mh = buf;
294
295 /* space for the header and at least one load command? */
296 if (bufsz < sizeof(kernel_mach_header_t) + sizeof(struct uuid_command)) {
297 AUTHPRNT("libkern image too small");
298 return NULL;
299 }
300
301 /* validate the mach header */
302 if (mh->magic != MH_MAGIC_64 || (mh->sizeofcmds > bufsz - sizeof(kernel_mach_header_t))) {
303 AUTHPRNT("invalid MachO header");
304 return NULL;
305 }
306
307 /* iterate the load commands */
308 size_t offset = sizeof(kernel_mach_header_t);
309 for (size_t i = 0; i < mh->ncmds; i++) {
310 cmd = buf + offset;
311
312 if (cmd->cmd == LC_UUID) {
313 *uuidsz = sizeof(cmd->uuid);
314 return &cmd->uuid;
315 }
316
317 if (os_add_overflow(cmd->cmdsize, offset, &offset) ||
318 offset > bufsz - sizeof(struct uuid_command)) {
319 return NULL;
320 }
321 }
322
323 return NULL;
324 }
325
326 /*
327 * Rev2 chunklist handling
328 */
329 const struct chunklist_pubkey rev2_chunklist_pubkeys[] = {
330 };
331 const size_t rev2_chunklist_num_pubkeys = sizeof(rev2_chunklist_pubkeys) / sizeof(rev2_chunklist_pubkeys[0]);
332
333 static const struct efi_guid_t gEfiSignAppleCertTypeGuid = CHUNKLIST_REV2_SIG_HASH_GUID;
334 static const struct efi_guid_t gEfiSignCertTypeRsa2048Sha256Guid = EFI_CERT_TYPE_RSA2048_SHA256;
335
336 static boolean_t
337 validate_rev2_certificate(struct rev2_chunklist_certificate *certificate)
338 {
339 /* Default value of current security epoch MUST be CHUNKLIST_MIN_SECURITY_EPOCH */
340 uint8_t current_security_epoch = CHUNKLIST_MIN_SECURITY_EPOCH;
341
342 /* Certificate.Length must be equal to sizeof(CERTIFICATE) */
343 if (certificate->length != sizeof(struct rev2_chunklist_certificate)) {
344 AUTHDBG("invalid certificate length");
345 return FALSE;
346 }
347
348 /* Certificate.Revision MUST be equal to 2 */
349 if (certificate->revision != 2) {
350 AUTHDBG("invalid certificate revision");
351 return FALSE;
352 }
353
354 /* Certificate.SecurityEpoch MUST be current or higher */
355 if (PE_parse_boot_argn(CHUNKLIST_SECURITY_EPOCH, &current_security_epoch, sizeof(current_security_epoch)) &&
356 certificate->security_epoch < current_security_epoch) {
357 AUTHDBG("invalid certificate security epoch");
358 return FALSE;
359 }
360
361 /* Certificate.CertificateType MUST be equal to WIN_CERT_TYPE_EFI_GUID (0x0EF1) */
362 if (certificate->certificate_type != WIN_CERT_TYPE_EFI_GUID) {
363 AUTHDBG("invalid certificate type");
364 return FALSE;
365 }
366
367 /* Certificate.CertificateGuid MUST be equal to 45E7BC51-913C-42AC-96A2-10712FFBEBA7 */
368 if (0 != memcmp(&certificate->certificate_guid, &gEfiSignAppleCertTypeGuid, sizeof(struct efi_guid_t))) {
369 AUTHDBG("invalid certificate GUID");
370 return FALSE;
371 }
372
373 /* Certificate.HashTypeGuid MUST be equal to A7717414-C616-4977-9420-844712A735BF */
374 if (0 != memcmp(&certificate->hash_type_guid, &gEfiSignCertTypeRsa2048Sha256Guid, sizeof(struct efi_guid_t))) {
375 AUTHDBG("invalid hash type GUID");
376 return FALSE;
377 }
378
379 return TRUE;
380 }
381
382 static int
383 validate_rev2_chunklist(uint8_t *buffer, size_t buffer_size)
384 {
385 struct rev2_chunklist_certificate *certificate;
386 size_t security_data_offset;
387
388 /* Check input parameters to be sane */
389 if (buffer == NULL || buffer_size == 0) {
390 AUTHDBG("invalid parameter");
391 return EINVAL;
392 }
393
394 /* Check for existing signature */
395 if (buffer_size < sizeof(struct rev2_chunklist_certificate)) {
396 AUTHDBG("no space for certificate");
397 return EINVAL;
398 }
399
400 security_data_offset = buffer_size - sizeof(struct rev2_chunklist_certificate);
401 certificate = (struct rev2_chunklist_certificate*)(buffer + security_data_offset);
402
403 /* Check signature candidate to be a valid rev2 chunklist certificate */
404 if (TRUE != validate_rev2_certificate(certificate)) {
405 return EINVAL;
406 }
407
408 /* Check public key to be trusted */
409 for (size_t i = 0; i < rev2_chunklist_num_pubkeys; i++) {
410 const struct chunklist_pubkey *key = &rev2_chunklist_pubkeys[i];
411 /* Production keys are always trusted */
412 if (key->is_production != TRUE) {
413 uint8_t no_rev2_dev = 0;
414 /* Do not trust rev2 development keys if CHUNKLIST_NO_REV2_DEV is present */
415 if (PE_parse_boot_argn(CHUNKLIST_NO_REV2_DEV, &no_rev2_dev, sizeof(no_rev2_dev))) {
416 AUTHDBG("rev2 development key is not trusted");
417 continue;
418 }
419 }
420
421 /* Check certificate public key to be the trusted one */
422 if (0 == memcmp(key->key, certificate->rsa_public_key, sizeof(certificate->rsa_public_key))) {
423 AUTHDBG("certificate public key is trusted");
424
425 /* Hash everything but signature */
426 SHA256_CTX hash_ctx;
427 SHA256_Init(&hash_ctx);
428 SHA256_Update(&hash_ctx, buffer, security_data_offset);
429
430 /* Include Certificate.SecurityEpoch value */
431 SHA256_Update(&hash_ctx, &certificate->security_epoch, sizeof(certificate->security_epoch));
432
433 /* Finalize hashing into the output buffer */
434 uint8_t sha_digest[SHA256_DIGEST_LENGTH];
435 SHA256_Final(sha_digest, &hash_ctx);
436
437 /* Validate signature */
438 return validate_signature(certificate->rsa_public_key,
439 sizeof(certificate->rsa_public_key),
440 certificate->rsa_signature,
441 sizeof(certificate->rsa_signature),
442 sha_digest);
443 }
444 }
445
446 AUTHDBG("certificate public key is not trusted");
447 return EINVAL;
448 }
449
450 /*
451 * Main chunklist validation routine
452 */
453 static int
454 validate_chunklist(void *buf, size_t len)
455 {
456 int err = 0;
457 size_t sigsz = 0;
458 size_t sig_end = 0;
459 size_t chunks_end = 0;
460 size_t sig_len = 0;
461 boolean_t valid_sig = FALSE;
462 struct chunklist_hdr *hdr = buf;
463
464 if (len < sizeof(struct chunklist_hdr)) {
465 AUTHPRNT("no space for header");
466 return EINVAL;
467 }
468
469 /* recognized file format? */
470 if (hdr->cl_magic != CHUNKLIST_MAGIC ||
471 hdr->cl_file_ver != CHUNKLIST_FILE_VERSION_10 ||
472 hdr->cl_chunk_method != CHUNKLIST_CHUNK_METHOD_10) {
473 AUTHPRNT("unrecognized chunklist format");
474 return EINVAL;
475 }
476
477 /* determine signature length based on signature method */
478 if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV1) {
479 AUTHPRNT("rev1 chunklist");
480 sig_len = CHUNKLIST_REV1_SIG_LEN;
481 } else if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV2) {
482 AUTHPRNT("rev2 chunklist");
483 sig_len = CHUNKLIST_REV2_SIG_LEN;
484 } else {
485 AUTHPRNT("unrecognized chunklist signature method");
486 return EINVAL;
487 }
488
489 /* does the chunk list fall within the bounds of the buffer? */
490 if (os_mul_and_add_overflow(hdr->cl_chunk_count, sizeof(struct chunklist_chunk), hdr->cl_chunk_offset, &chunks_end) ||
491 hdr->cl_chunk_offset < sizeof(struct chunklist_hdr) || chunks_end > len) {
492 AUTHPRNT("invalid chunk_count (%llu) or chunk_offset (%llu)",
493 hdr->cl_chunk_count, hdr->cl_chunk_offset);
494 return EINVAL;
495 }
496
497 /* does the signature fall within the bounds of the buffer? */
498 if (os_add_overflow(hdr->cl_sig_offset, sig_len, &sig_end) ||
499 hdr->cl_sig_offset < sizeof(struct chunklist_hdr) ||
500 hdr->cl_sig_offset < chunks_end ||
501 hdr->cl_sig_offset > len) {
502 AUTHPRNT("invalid signature offset (%llu)", hdr->cl_sig_offset);
503 return EINVAL;
504 }
505
506 if (sig_end > len ||
507 os_sub_overflow(len, hdr->cl_sig_offset, &sigsz) ||
508 sigsz != sig_len) {
509 /* missing or incorrect signature size */
510 return EINVAL;
511 }
512
513 /* validate rev1 chunklist */
514 if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV1) {
515 /* Do not trust rev1 chunklists if CHUNKLIST_NO_REV1 is present */
516 uint8_t no_rev1;
517 if (PE_parse_boot_argn(CHUNKLIST_NO_REV1, &no_rev1, sizeof(no_rev1))) {
518 AUTHDBG("rev1 chunklists are not trusted");
519 return EINVAL;
520 }
521
522 /* hash the chunklist (excluding the signature) */
523 AUTHDBG("hashing rev1 chunklist");
524 uint8_t sha_digest[SHA256_DIGEST_LENGTH];
525 SHA256_CTX sha_ctx;
526 SHA256_Init(&sha_ctx);
527 SHA256_Update(&sha_ctx, buf, hdr->cl_sig_offset);
528 SHA256_Final(sha_digest, &sha_ctx);
529
530 AUTHDBG("validating rev1 chunklist signature against rev1 pub keys");
531 for (size_t i = 0; i < rev1_chunklist_num_pubkeys; i++) {
532 const struct chunklist_pubkey *key = &rev1_chunklist_pubkeys[i];
533 err = validate_signature(key->key, CHUNKLIST_PUBKEY_LEN, buf + hdr->cl_sig_offset, CHUNKLIST_SIGNATURE_LEN, sha_digest);
534 if (err == 0) {
535 AUTHDBG("validated rev1 chunklist signature with rev1 key %lu (prod=%d)", i, key->is_production);
536 valid_sig = key->is_production;
537 #if IMAGEBOOT_ALLOW_DEVKEYS
538 if (!key->is_production) {
539 /* allow dev keys in dev builds only */
540 AUTHDBG("*** allowing DEV rev1 key: this will fail in customer builds ***");
541 valid_sig = TRUE;
542 }
543 #endif
544 goto out;
545 }
546 }
547
548 /* At this point we tried all the keys: nothing went wrong but none of them
549 * signed our chunklist. */
550 AUTHPRNT("rev1 signature did not verify against any known rev1 public key");
551 } else if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV2) {
552 AUTHDBG("validating rev2 chunklist signature against rev2 pub keys");
553 err = validate_rev2_chunklist(buf, len);
554 if (err) {
555 goto out;
556 }
557 valid_sig = TRUE;
558 }
559
560 out:
561 if (err) {
562 return err;
563 } else if (valid_sig == TRUE) {
564 return 0; /* signed, and everything checked out */
565 } else {
566 return EINVAL;
567 }
568 }
569
570 /*
571 * Authenticate a given DMG file using chunklist
572 */
573 int
574 authenticate_root_with_chunklist(const char *rootdmg_path, boolean_t *out_enforced)
575 {
576 char *chunklist_path = NULL;
577 void *chunklist_buf = NULL;
578 size_t chunklist_len = 32 * 1024 * 1024UL;
579 boolean_t enforced = TRUE;
580 int err = 0;
581
582 chunklist_path = zalloc(ZV_NAMEI);
583 err = construct_chunklist_path(chunklist_path, rootdmg_path);
584 if (err) {
585 AUTHPRNT("failed creating chunklist path");
586 goto out;
587 }
588
589 AUTHDBG("validating root against chunklist %s", chunklist_path);
590
591 /*
592 * Read and authenticate the chunklist, then validate the root image against
593 * the chunklist.
594 */
595 AUTHDBG("reading chunklist");
596 err = imageboot_read_file(KHEAP_TEMP, chunklist_path, &chunklist_buf, &chunklist_len);
597 if (err) {
598 AUTHPRNT("failed to read chunklist");
599 goto out;
600 }
601
602 AUTHDBG("validating chunklist");
603 err = validate_chunklist(chunklist_buf, chunklist_len);
604 if (err) {
605 AUTHPRNT("failed to validate chunklist");
606 goto out;
607 }
608 AUTHDBG("successfully validated chunklist");
609
610 AUTHDBG("validating root image against chunklist");
611 err = validate_root_image(rootdmg_path, chunklist_buf);
612 if (err) {
613 AUTHPRNT("failed to validate root image against chunklist (%d)", err);
614 goto out;
615 }
616
617 /* everything checked out - go ahead and mount this */
618 AUTHDBG("root image authenticated");
619
620 out:
621 #if CONFIG_CSR
622 if (err && (csr_check(CSR_ALLOW_ANY_RECOVERY_OS) == 0)) {
623 AUTHPRNT("CSR_ALLOW_ANY_RECOVERY_OS set, allowing unauthenticated root image");
624 err = 0;
625 enforced = FALSE;
626 }
627 #endif
628
629 if (out_enforced != NULL) {
630 *out_enforced = enforced;
631 }
632 kheap_free_safe(KHEAP_TEMP, chunklist_buf, chunklist_len);
633 zfree(ZV_NAMEI, chunklist_path);
634 return err;
635 }
636
637 int
638 authenticate_root_version_check(void)
639 {
640 kc_format_t kc_format;
641 if (PE_get_primary_kc_format(&kc_format) && kc_format == KCFormatFileset) {
642 return authenticate_bootkc_uuid();
643 } else {
644 return authenticate_libkern_uuid();
645 }
646 }
647
648 /*
649 * Check that the UUID of the boot KC currently loaded matches the one on disk.
650 */
651 int
652 authenticate_bootkc_uuid(void)
653 {
654 int err = 0;
655 void *buf = NULL;
656 size_t bufsz = 1 * 1024 * 1024UL;
657
658 /* get the UUID of the bootkc in /S/L/KC */
659 err = imageboot_read_file(KHEAP_TEMP, bootkc_path, &buf, &bufsz);
660 if (err) {
661 goto out;
662 }
663
664 unsigned long uuidsz = 0;
665 const uuid_t *img_uuid = getuuidfromheader_safe(buf, bufsz, &uuidsz);
666 if (img_uuid == NULL || uuidsz != sizeof(uuid_t)) {
667 AUTHPRNT("invalid UUID (sz = %lu)", uuidsz);
668 err = EINVAL;
669 goto out;
670 }
671
672 if (!kernelcache_uuid_valid) {
673 AUTHPRNT("Boot KC UUID was not set at boot.");
674 err = EINVAL;
675 goto out;
676 }
677
678 /* ... and compare them */
679 if (bcmp(&kernelcache_uuid, img_uuid, uuidsz) != 0) {
680 AUTHPRNT("UUID of running bootkc does not match %s", bootkc_path);
681
682 uuid_string_t img_uuid_str, live_uuid_str;
683 uuid_unparse(*img_uuid, img_uuid_str);
684 uuid_unparse(kernelcache_uuid, live_uuid_str);
685 AUTHPRNT("loaded bootkc UUID = %s", live_uuid_str);
686 AUTHPRNT("on-disk bootkc UUID = %s", img_uuid_str);
687
688 err = EINVAL;
689 goto out;
690 }
691
692 /* UUID matches! */
693 out:
694 kheap_free_safe(KHEAP_TEMP, buf, bufsz);
695 return err;
696 }
697
698 /*
699 * Check that the UUID of the libkern currently loaded matches the one on disk.
700 */
701 int
702 authenticate_libkern_uuid(void)
703 {
704 int err = 0;
705 void *buf = NULL;
706 size_t bufsz = 4 * 1024 * 1024UL;
707
708 /* get the UUID of the libkern in /S/L/E */
709 err = imageboot_read_file(KHEAP_TEMP, libkern_path, &buf, &bufsz);
710 if (err) {
711 goto out;
712 }
713
714 if (fatfile_validate_fatarches((vm_offset_t)buf, bufsz) == LOAD_SUCCESS) {
715 struct fat_header *fat_header = buf;
716 struct fat_arch fat_arch;
717 if (fatfile_getbestarch((vm_offset_t)fat_header, bufsz, NULL, &fat_arch, FALSE) != LOAD_SUCCESS) {
718 err = EINVAL;
719 goto out;
720 }
721 kheap_free_safe(KHEAP_TEMP, buf, bufsz);
722 buf = NULL;
723 bufsz = MIN(fat_arch.size, 4 * 1024 * 1024UL);
724 err = imageboot_read_file_from_offset(KHEAP_TEMP, libkern_path, fat_arch.offset, &buf, &bufsz);
725 if (err) {
726 goto out;
727 }
728 }
729
730 unsigned long uuidsz = 0;
731 const uuid_t *img_uuid = getuuidfromheader_safe(buf, bufsz, &uuidsz);
732 if (img_uuid == NULL || uuidsz != sizeof(uuid_t)) {
733 AUTHPRNT("invalid UUID (sz = %lu)", uuidsz);
734 err = EINVAL;
735 goto out;
736 }
737
738 /* Get the UUID of the loaded libkern */
739 uuid_t live_uuid;
740 err = OSKextGetUUIDForName(libkern_bundle, live_uuid);
741 if (err) {
742 AUTHPRNT("could not find loaded libkern");
743 goto out;
744 }
745
746 /* ... and compare them */
747 if (bcmp(live_uuid, img_uuid, uuidsz) != 0) {
748 AUTHPRNT("UUID of running libkern does not match %s", libkern_path);
749
750 uuid_string_t img_uuid_str, live_uuid_str;
751 uuid_unparse(*img_uuid, img_uuid_str);
752 uuid_unparse(live_uuid, live_uuid_str);
753 AUTHPRNT("loaded libkern UUID = %s", live_uuid_str);
754 AUTHPRNT("on-disk libkern UUID = %s", img_uuid_str);
755
756 err = EINVAL;
757 goto out;
758 }
759
760 /* UUID matches! */
761 out:
762 kheap_free_safe(KHEAP_TEMP, buf, bufsz);
763 return err;
764 }