2 * Copyright (c) 2019-2020 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
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>
41 #include <sys/namei.h>
42 #include <sys/fcntl.h>
43 #include <sys/vnode.h>
44 #include <sys/sysproto.h>
46 #include <miscfs/devfs/devfsdefs.h>
47 #include <libkern/crypto/sha2.h>
48 #include <libkern/crypto/rsa.h>
49 #include <libkern/OSKextLibPrivate.h>
51 #include <kern/chunklist.h>
52 #include <kern/kalloc.h>
54 #include <pexpert/pexpert.h>
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)
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";
63 extern boolean_t kernelcache_uuid_valid
;
64 extern uuid_t kernelcache_uuid
;
67 static const char *bootkc_path
= "/System/Library/KernelCollections/BootKernelExtensions.kc.debug";
69 static const char *bootkc_path
= "/System/Library/KernelCollections/BootKernelExtensions.kc.kasan";
71 static const char *bootkc_path
= "/System/Library/KernelCollections/BootKernelExtensions.kc.development";
73 static const char *bootkc_path
= "/System/Library/KernelCollections/BootKernelExtensions.kc";
77 * Rev1 chunklist handling
79 const struct chunklist_pubkey rev1_chunklist_pubkeys
[] = {
81 const size_t rev1_chunklist_num_pubkeys
= sizeof(rev1_chunklist_pubkeys
) / sizeof(rev1_chunklist_pubkeys
[0]);
84 key_byteswap(void *_dst
, const void *_src
, size_t len
)
86 uint32_t *dst
__attribute__((align_value(1))) = _dst
;
87 const uint32_t *src
__attribute__((align_value(1))) = _src
;
89 assert(len
% sizeof(uint32_t) == 0);
91 len
= len
/ sizeof(uint32_t);
92 for (size_t i
= 0; i
< len
; i
++) {
93 dst
[len
- i
- 1] = OSSwapInt32(src
[i
]);
98 construct_chunklist_path(char path
[static MAXPATHLEN
], const char *root_path
)
102 len
= strnlen(root_path
, MAXPATHLEN
);
103 if (len
< MAXPATHLEN
&& len
> strlen(".dmg")) {
104 /* correctly terminated string with space for extension */
106 AUTHPRNT("malformed root path");
110 len
= strlcpy(path
, root_path
, MAXPATHLEN
);
111 if (len
>= MAXPATHLEN
) {
112 AUTHPRNT("root path is too long");
116 path
[len
- strlen(".dmg")] = '\0';
117 len
= strlcat(path
, ".chunklist", MAXPATHLEN
);
118 if (len
>= MAXPATHLEN
) {
119 AUTHPRNT("chunklist path is too long");
127 validate_signature(const uint8_t *key_msb
, size_t keylen
, uint8_t *sig_msb
, size_t siglen
, uint8_t *digest
)
130 bool sig_valid
= false;
133 const uint8_t exponent
[] = { 0x01, 0x00, 0x01 };
134 rsa_pub_ctx
*rsa_ctx
;
138 modulus
= kheap_alloc(KHEAP_TEMP
, keylen
, Z_WAITOK
| Z_ZERO
);
139 rsa_ctx
= kheap_alloc(KHEAP_TEMP
, sizeof(rsa_pub_ctx
),
141 sig
= kheap_alloc(KHEAP_TEMP
, siglen
, Z_WAITOK
| Z_ZERO
);
143 if (modulus
== NULL
|| rsa_ctx
== NULL
|| sig
== NULL
) {
148 key_byteswap(modulus
, key_msb
, keylen
);
149 key_byteswap(sig
, sig_msb
, siglen
);
151 err
= rsa_make_pub(rsa_ctx
,
152 sizeof(exponent
), exponent
,
153 CHUNKLIST_PUBKEY_LEN
, modulus
);
155 AUTHPRNT("rsa_make_pub() failed");
159 err
= rsa_verify_pkcs1v15(rsa_ctx
, CC_DIGEST_OID_SHA256
,
160 SHA256_DIGEST_LENGTH
, digest
,
165 AUTHPRNT("rsa_verify() failed");
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
);
176 } else if (sig_valid
== true) {
177 return 0; /* success */
184 validate_root_image(const char *root_path
, void *chunklist
)
187 struct chunklist_hdr
*hdr
= chunklist
;
188 struct chunklist_chunk
*chk
= NULL
;
190 struct vnode
*vp
= NULL
;
193 bool doclose
= false;
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
);
201 AUTHDBG("validating root dmg %s", root_path
);
203 vp
= imgboot_get_image_file(root_path
, &fsize
, &err
);
208 if ((err
= VNOP_OPEN(vp
, FREAD
, ctx
)) != 0) {
209 AUTHPRNT("failed to open vnode");
215 * Iterate the chunk list and check each chunk
217 chk
= chunklist
+ hdr
->cl_chunk_offset
;
218 for (ch
= 0; ch
< hdr
->cl_chunk_count
; ch
++) {
222 /* allocate buffer based on first chunk size */
223 buf
= kheap_alloc(KHEAP_TEMP
, chk
->chunk_size
, Z_WAITOK
);
228 bufsz
= chk
->chunk_size
;
231 if (chk
->chunk_size
> bufsz
) {
232 AUTHPRNT("chunk size too big");
237 err
= vn_rdwr(UIO_READ
, vp
, (caddr_t
)buf
, chk
->chunk_size
,
238 offset
, UIO_SYSSPACE
, IO_NODELOCKED
, kerncred
, &resid
, p
);
240 AUTHPRNT("vn_rdrw fail (err = %d, resid = %d)", err
, resid
);
245 AUTHPRNT("chunk covered non-existant part of image");
249 /* calculate the SHA256 of this chunk */
250 uint8_t sha_digest
[SHA256_DIGEST_LENGTH
];
252 SHA256_Init(&sha_ctx
);
253 SHA256_Update(&sha_ctx
, buf
, chk
->chunk_size
);
254 SHA256_Final(sha_digest
, &sha_ctx
);
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
);
263 if (os_add_overflow(offset
, chk
->chunk_size
, &offset
)) {
270 if (offset
!= fsize
) {
271 AUTHPRNT("chunklist did not cover entire file (offset = %lld, fsize = %lld)", offset
, fsize
);
277 kheap_free_safe(KHEAP_TEMP
, buf
, bufsz
);
279 VNOP_CLOSE(vp
, FREAD
, ctx
);
289 static const uuid_t
*
290 getuuidfromheader_safe(const void *buf
, size_t bufsz
, size_t *uuidsz
)
292 const struct uuid_command
*cmd
= NULL
;
293 const kernel_mach_header_t
*mh
= buf
;
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");
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");
307 /* iterate the load commands */
308 size_t offset
= sizeof(kernel_mach_header_t
);
309 for (size_t i
= 0; i
< mh
->ncmds
; i
++) {
312 if (cmd
->cmd
== LC_UUID
) {
313 *uuidsz
= sizeof(cmd
->uuid
);
317 if (os_add_overflow(cmd
->cmdsize
, offset
, &offset
) ||
318 offset
> bufsz
- sizeof(struct uuid_command
)) {
327 * Rev2 chunklist handling
329 const struct chunklist_pubkey rev2_chunklist_pubkeys
[] = {
331 const size_t rev2_chunklist_num_pubkeys
= sizeof(rev2_chunklist_pubkeys
) / sizeof(rev2_chunklist_pubkeys
[0]);
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
;
337 validate_rev2_certificate(struct rev2_chunklist_certificate
*certificate
)
339 /* Default value of current security epoch MUST be CHUNKLIST_MIN_SECURITY_EPOCH */
340 uint8_t current_security_epoch
= CHUNKLIST_MIN_SECURITY_EPOCH
;
342 /* Certificate.Length must be equal to sizeof(CERTIFICATE) */
343 if (certificate
->length
!= sizeof(struct rev2_chunklist_certificate
)) {
344 AUTHDBG("invalid certificate length");
348 /* Certificate.Revision MUST be equal to 2 */
349 if (certificate
->revision
!= 2) {
350 AUTHDBG("invalid certificate revision");
354 /* Certificate.SecurityEpoch MUST be current or higher */
355 if (PE_parse_boot_argn(CHUNKLIST_SECURITY_EPOCH
, ¤t_security_epoch
, sizeof(current_security_epoch
)) &&
356 certificate
->security_epoch
< current_security_epoch
) {
357 AUTHDBG("invalid certificate security epoch");
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");
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");
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");
383 validate_rev2_chunklist(uint8_t *buffer
, size_t buffer_size
)
385 struct rev2_chunklist_certificate
*certificate
;
386 size_t security_data_offset
;
388 /* Check input parameters to be sane */
389 if (buffer
== NULL
|| buffer_size
== 0) {
390 AUTHDBG("invalid parameter");
394 /* Check for existing signature */
395 if (buffer_size
< sizeof(struct rev2_chunklist_certificate
)) {
396 AUTHDBG("no space for certificate");
400 security_data_offset
= buffer_size
- sizeof(struct rev2_chunklist_certificate
);
401 certificate
= (struct rev2_chunklist_certificate
*)(buffer
+ security_data_offset
);
403 /* Check signature candidate to be a valid rev2 chunklist certificate */
404 if (TRUE
!= validate_rev2_certificate(certificate
)) {
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");
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");
425 /* Hash everything but signature */
427 SHA256_Init(&hash_ctx
);
428 SHA256_Update(&hash_ctx
, buffer
, security_data_offset
);
430 /* Include Certificate.SecurityEpoch value */
431 SHA256_Update(&hash_ctx
, &certificate
->security_epoch
, sizeof(certificate
->security_epoch
));
433 /* Finalize hashing into the output buffer */
434 uint8_t sha_digest
[SHA256_DIGEST_LENGTH
];
435 SHA256_Final(sha_digest
, &hash_ctx
);
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
),
446 AUTHDBG("certificate public key is not trusted");
451 * Main chunklist validation routine
454 validate_chunklist(void *buf
, size_t len
)
459 size_t chunks_end
= 0;
461 boolean_t valid_sig
= FALSE
;
462 struct chunklist_hdr
*hdr
= buf
;
464 if (len
< sizeof(struct chunklist_hdr
)) {
465 AUTHPRNT("no space for header");
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");
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
;
485 AUTHPRNT("unrecognized chunklist signature method");
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
);
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
);
507 os_sub_overflow(len
, hdr
->cl_sig_offset
, &sigsz
) ||
509 /* missing or incorrect signature size */
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 */
517 if (PE_parse_boot_argn(CHUNKLIST_NO_REV1
, &no_rev1
, sizeof(no_rev1
))) {
518 AUTHDBG("rev1 chunklists are not trusted");
522 /* hash the chunklist (excluding the signature) */
523 AUTHDBG("hashing rev1 chunklist");
524 uint8_t sha_digest
[SHA256_DIGEST_LENGTH
];
526 SHA256_Init(&sha_ctx
);
527 SHA256_Update(&sha_ctx
, buf
, hdr
->cl_sig_offset
);
528 SHA256_Final(sha_digest
, &sha_ctx
);
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
);
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 ***");
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
);
563 } else if (valid_sig
== TRUE
) {
564 return 0; /* signed, and everything checked out */
571 * Authenticate a given DMG file using chunklist
574 authenticate_root_with_chunklist(const char *rootdmg_path
, boolean_t
*out_enforced
)
576 char *chunklist_path
= NULL
;
577 void *chunklist_buf
= NULL
;
578 size_t chunklist_len
= 32 * 1024 * 1024UL;
579 boolean_t enforced
= TRUE
;
582 chunklist_path
= zalloc(ZV_NAMEI
);
583 err
= construct_chunklist_path(chunklist_path
, rootdmg_path
);
585 AUTHPRNT("failed creating chunklist path");
589 AUTHDBG("validating root against chunklist %s", chunklist_path
);
592 * Read and authenticate the chunklist, then validate the root image against
595 AUTHDBG("reading chunklist");
596 err
= imageboot_read_file(KHEAP_TEMP
, chunklist_path
, &chunklist_buf
, &chunklist_len
);
598 AUTHPRNT("failed to read chunklist");
602 AUTHDBG("validating chunklist");
603 err
= validate_chunklist(chunklist_buf
, chunklist_len
);
605 AUTHPRNT("failed to validate chunklist");
608 AUTHDBG("successfully validated chunklist");
610 AUTHDBG("validating root image against chunklist");
611 err
= validate_root_image(rootdmg_path
, chunklist_buf
);
613 AUTHPRNT("failed to validate root image against chunklist (%d)", err
);
617 /* everything checked out - go ahead and mount this */
618 AUTHDBG("root image authenticated");
622 if (err
&& (csr_check(CSR_ALLOW_ANY_RECOVERY_OS
) == 0)) {
623 AUTHPRNT("CSR_ALLOW_ANY_RECOVERY_OS set, allowing unauthenticated root image");
629 if (out_enforced
!= NULL
) {
630 *out_enforced
= enforced
;
632 kheap_free_safe(KHEAP_TEMP
, chunklist_buf
, chunklist_len
);
633 zfree(ZV_NAMEI
, chunklist_path
);
638 authenticate_root_version_check(void)
640 kc_format_t kc_format
;
641 if (PE_get_primary_kc_format(&kc_format
) && kc_format
== KCFormatFileset
) {
642 return authenticate_bootkc_uuid();
644 return authenticate_libkern_uuid();
649 * Check that the UUID of the boot KC currently loaded matches the one on disk.
652 authenticate_bootkc_uuid(void)
656 size_t bufsz
= 1 * 1024 * 1024UL;
658 /* get the UUID of the bootkc in /S/L/KC */
659 err
= imageboot_read_file(KHEAP_TEMP
, bootkc_path
, &buf
, &bufsz
);
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
);
672 if (!kernelcache_uuid_valid
) {
673 AUTHPRNT("Boot KC UUID was not set at boot.");
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
);
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
);
694 kheap_free_safe(KHEAP_TEMP
, buf
, bufsz
);
699 * Check that the UUID of the libkern currently loaded matches the one on disk.
702 authenticate_libkern_uuid(void)
706 size_t bufsz
= 4 * 1024 * 1024UL;
708 /* get the UUID of the libkern in /S/L/E */
709 err
= imageboot_read_file(KHEAP_TEMP
, libkern_path
, &buf
, &bufsz
);
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
) {
721 kheap_free_safe(KHEAP_TEMP
, buf
, bufsz
);
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
);
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
);
738 /* Get the UUID of the loaded libkern */
740 err
= OSKextGetUUIDForName(libkern_bundle
, live_uuid
);
742 AUTHPRNT("could not find loaded libkern");
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
);
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
);
762 kheap_free_safe(KHEAP_TEMP
, buf
, bufsz
);