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