xref: /xnu-8020.121.3/bsd/kern/chunklist.c (revision fdd8201d7b966f0c3ea610489d29bd841d358941)
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 
59 static const char *libkern_path = "/System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern";
60 static const char *libkern_bundle = "com.apple.kpi.libkern";
61 
62 extern boolean_t kernelcache_uuid_valid;
63 extern uuid_t kernelcache_uuid;
64 
65 #if DEBUG
66 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc.debug";
67 #elif KASAN
68 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc.kasan";
69 #elif DEVELOPMENT
70 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc.development";
71 #else
72 static const char *bootkc_path = "/System/Library/KernelCollections/BootKernelExtensions.kc";
73 #endif
74 
75 /*
76  * Rev1 chunklist handling
77  */
78 const struct chunklist_pubkey rev1_chunklist_pubkeys[] = {
79 };
80 const size_t rev1_chunklist_num_pubkeys = sizeof(rev1_chunklist_pubkeys) / sizeof(rev1_chunklist_pubkeys[0]);
81 
82 static void
key_byteswap(void * _dst,const void * _src,size_t len)83 key_byteswap(void *_dst, const void *_src, size_t len)
84 {
85 	uint32_t *dst __attribute__((align_value(1))) = _dst;
86 	const uint32_t *src __attribute__((align_value(1))) = _src;
87 
88 	assert(len % sizeof(uint32_t) == 0);
89 
90 	len = len / sizeof(uint32_t);
91 	for (size_t i = 0; i < len; i++) {
92 		dst[len - i - 1] = OSSwapInt32(src[i]);
93 	}
94 }
95 
96 static int
construct_chunklist_path(char path[static MAXPATHLEN],const char * root_path)97 construct_chunklist_path(char path[static MAXPATHLEN], const char *root_path)
98 {
99 	size_t len = 0;
100 
101 	len = strnlen(root_path, MAXPATHLEN);
102 	if (len < MAXPATHLEN && len > strlen(".dmg")) {
103 		/* correctly terminated string with space for extension */
104 	} else {
105 		AUTHPRNT("malformed root path");
106 		return EOVERFLOW;
107 	}
108 
109 	len = strlcpy(path, root_path, MAXPATHLEN);
110 	if (len >= MAXPATHLEN) {
111 		AUTHPRNT("root path is too long");
112 		return EOVERFLOW;
113 	}
114 
115 	path[len - strlen(".dmg")] = '\0';
116 	len = strlcat(path, ".chunklist", MAXPATHLEN);
117 	if (len >= MAXPATHLEN) {
118 		AUTHPRNT("chunklist path is too long");
119 		return EOVERFLOW;
120 	}
121 
122 	return 0;
123 }
124 
125 static int
validate_signature(const uint8_t * key_msb,size_t keylen,uint8_t * sig_msb,size_t siglen,uint8_t * digest)126 validate_signature(const uint8_t *key_msb, size_t keylen, uint8_t *sig_msb, size_t siglen, uint8_t *digest)
127 {
128 	int err = 0;
129 	bool sig_valid = false;
130 	uint8_t *sig = NULL;
131 
132 	const uint8_t exponent[] = { 0x01, 0x00, 0x01 };
133 	rsa_pub_ctx *rsa_ctx;
134 	uint8_t *modulus;
135 
136 
137 	rsa_ctx = kalloc_type(rsa_pub_ctx,
138 	    Z_WAITOK | Z_ZERO | Z_NOFAIL);
139 	modulus = (uint8_t *)kalloc_data(keylen, Z_WAITOK | Z_ZERO);
140 	sig = (uint8_t *)kalloc_data(siglen, Z_WAITOK | Z_ZERO);
141 
142 	if (modulus == NULL || sig == NULL) {
143 		err = ENOMEM;
144 		goto out;
145 	}
146 
147 	key_byteswap(modulus, key_msb, keylen);
148 	key_byteswap(sig, sig_msb, siglen);
149 
150 	err = rsa_make_pub(rsa_ctx,
151 	    sizeof(exponent), exponent,
152 	    CHUNKLIST_PUBKEY_LEN, modulus);
153 	if (err) {
154 		AUTHPRNT("rsa_make_pub() failed");
155 		goto out;
156 	}
157 
158 	err = rsa_verify_pkcs1v15(rsa_ctx, CC_DIGEST_OID_SHA256,
159 	    SHA256_DIGEST_LENGTH, digest,
160 	    siglen, sig,
161 	    &sig_valid);
162 	if (err) {
163 		sig_valid = false;
164 		AUTHPRNT("rsa_verify() failed");
165 		goto out;
166 	}
167 
168 out:
169 	kfree_data(sig, siglen);
170 	kfree_type(rsa_pub_ctx, rsa_ctx);
171 	kfree_data(modulus, keylen);
172 
173 	if (err) {
174 		return err;
175 	} else if (sig_valid == true) {
176 		return 0; /* success */
177 	} else {
178 		return EAUTH;
179 	}
180 }
181 
182 static int
validate_root_image(const char * root_path,void * chunklist)183 validate_root_image(const char *root_path, void *chunklist)
184 {
185 	int err = 0;
186 	struct chunklist_hdr *hdr = chunklist;
187 	struct chunklist_chunk *chk = NULL;
188 	size_t ch = 0;
189 	struct vnode *vp = NULL;
190 	off_t fsize = 0;
191 	off_t offset = 0;
192 	bool doclose = false;
193 	size_t bufsz = 0;
194 	void *buf = NULL;
195 
196 	vfs_context_t ctx = vfs_context_kernel();
197 	kauth_cred_t kerncred = vfs_context_ucred(ctx);
198 	proc_t p = vfs_context_proc(ctx);
199 
200 	AUTHDBG("validating root dmg %s", root_path);
201 
202 	vp = imgboot_get_image_file(root_path, &fsize, &err);
203 	if (vp == NULL) {
204 		goto out;
205 	}
206 
207 	if ((err = VNOP_OPEN(vp, FREAD, ctx)) != 0) {
208 		AUTHPRNT("failed to open vnode");
209 		goto out;
210 	}
211 	doclose = true;
212 
213 	/*
214 	 * Iterate the chunk list and check each chunk
215 	 */
216 	chk = (struct chunklist_chunk *)((uintptr_t)chunklist + hdr->cl_chunk_offset);
217 	for (ch = 0; ch < hdr->cl_chunk_count; ch++) {
218 		int resid = 0;
219 
220 		if (!buf) {
221 			/* allocate buffer based on first chunk size */
222 			buf = kalloc_data(chk->chunk_size, Z_WAITOK);
223 			if (buf == NULL) {
224 				err = ENOMEM;
225 				goto out;
226 			}
227 			bufsz = chk->chunk_size;
228 		}
229 
230 		if (chk->chunk_size > bufsz) {
231 			AUTHPRNT("chunk size too big");
232 			err = EINVAL;
233 			goto out;
234 		}
235 
236 		err = vn_rdwr(UIO_READ, vp, (caddr_t)buf, chk->chunk_size,
237 		    offset, UIO_SYSSPACE, IO_NODELOCKED, kerncred, &resid, p);
238 		if (err) {
239 			AUTHPRNT("vn_rdrw fail (err = %d, resid = %d)", err, resid);
240 			goto out;
241 		}
242 		if (resid) {
243 			err = EINVAL;
244 			AUTHPRNT("chunk covered non-existant part of image");
245 			goto out;
246 		}
247 
248 		/* calculate the SHA256 of this chunk */
249 		uint8_t sha_digest[SHA256_DIGEST_LENGTH];
250 		SHA256_CTX sha_ctx;
251 		SHA256_Init(&sha_ctx);
252 		SHA256_Update(&sha_ctx, buf, chk->chunk_size);
253 		SHA256_Final(sha_digest, &sha_ctx);
254 
255 		/* Check the calculated SHA matches the chunk list */
256 		if (bcmp(sha_digest, chk->chunk_sha256, SHA256_DIGEST_LENGTH) != 0) {
257 			AUTHPRNT("SHA mismatch on chunk %lu (offset %lld, size %u)", ch, offset, chk->chunk_size);
258 			err = EINVAL;
259 			goto out;
260 		}
261 
262 		if (os_add_overflow(offset, chk->chunk_size, &offset)) {
263 			err = EINVAL;
264 			goto out;
265 		}
266 		chk++;
267 	}
268 
269 	if (offset != fsize) {
270 		AUTHPRNT("chunklist did not cover entire file (offset = %lld, fsize = %lld)", offset, fsize);
271 		err = EINVAL;
272 		goto out;
273 	}
274 
275 out:
276 	kfree_data(buf, bufsz);
277 	if (doclose) {
278 		VNOP_CLOSE(vp, FREAD, ctx);
279 	}
280 	if (vp) {
281 		vnode_put(vp);
282 		vp = NULL;
283 	}
284 
285 	return err;
286 }
287 
288 static const uuid_t *
getuuidfromheader_safe(const void * buf,size_t bufsz,size_t * uuidsz)289 getuuidfromheader_safe(const void *buf, size_t bufsz, size_t *uuidsz)
290 {
291 	const struct uuid_command *cmd = NULL;
292 	const kernel_mach_header_t *mh = buf;
293 
294 	/* space for the header and at least one load command? */
295 	if (bufsz < sizeof(kernel_mach_header_t) + sizeof(struct uuid_command)) {
296 		AUTHPRNT("libkern image too small");
297 		return NULL;
298 	}
299 
300 	/* validate the mach header */
301 	if (mh->magic != MH_MAGIC_64 || (mh->sizeofcmds > bufsz - sizeof(kernel_mach_header_t))) {
302 		AUTHPRNT("invalid MachO header");
303 		return NULL;
304 	}
305 
306 	/* iterate the load commands */
307 	size_t offset = sizeof(kernel_mach_header_t);
308 	for (size_t i = 0; i < mh->ncmds; i++) {
309 		cmd = (const struct uuid_command *)((uintptr_t)buf + offset);
310 
311 		if (cmd->cmd == LC_UUID) {
312 			*uuidsz = sizeof(cmd->uuid);
313 			return &cmd->uuid;
314 		}
315 
316 		if (os_add_overflow(cmd->cmdsize, offset, &offset) ||
317 		    offset > bufsz - sizeof(struct uuid_command)) {
318 			return NULL;
319 		}
320 	}
321 
322 	return NULL;
323 }
324 
325 /*
326  * Rev2 chunklist handling
327  */
328 const struct chunklist_pubkey rev2_chunklist_pubkeys[] = {
329 };
330 const size_t rev2_chunklist_num_pubkeys = sizeof(rev2_chunklist_pubkeys) / sizeof(rev2_chunklist_pubkeys[0]);
331 
332 static const struct efi_guid_t gEfiSignAppleCertTypeGuid = CHUNKLIST_REV2_SIG_HASH_GUID;
333 static const struct efi_guid_t gEfiSignCertTypeRsa2048Sha256Guid = EFI_CERT_TYPE_RSA2048_SHA256;
334 
335 static boolean_t
validate_rev2_certificate(struct rev2_chunklist_certificate * certificate)336 validate_rev2_certificate(struct rev2_chunklist_certificate *certificate)
337 {
338 	/* Default value of current security epoch MUST be CHUNKLIST_MIN_SECURITY_EPOCH */
339 	uint8_t current_security_epoch = CHUNKLIST_MIN_SECURITY_EPOCH;
340 
341 	/* Certificate.Length must be equal to sizeof(CERTIFICATE) */
342 	if (certificate->length != sizeof(struct rev2_chunklist_certificate)) {
343 		AUTHDBG("invalid certificate length");
344 		return FALSE;
345 	}
346 
347 	/* Certificate.Revision MUST be equal to 2 */
348 	if (certificate->revision != 2) {
349 		AUTHDBG("invalid certificate revision");
350 		return FALSE;
351 	}
352 
353 	/* Certificate.SecurityEpoch MUST be current or higher */
354 	if (PE_parse_boot_argn(CHUNKLIST_SECURITY_EPOCH, &current_security_epoch, sizeof(current_security_epoch)) &&
355 	    certificate->security_epoch < current_security_epoch) {
356 		AUTHDBG("invalid certificate security epoch");
357 		return FALSE;
358 	}
359 
360 	/* Certificate.CertificateType MUST be equal to WIN_CERT_TYPE_EFI_GUID (0x0EF1) */
361 	if (certificate->certificate_type != WIN_CERT_TYPE_EFI_GUID) {
362 		AUTHDBG("invalid certificate type");
363 		return FALSE;
364 	}
365 
366 	/* Certificate.CertificateGuid MUST be equal to 45E7BC51-913C-42AC-96A2-10712FFBEBA7 */
367 	if (0 != memcmp(&certificate->certificate_guid, &gEfiSignAppleCertTypeGuid, sizeof(struct efi_guid_t))) {
368 		AUTHDBG("invalid certificate GUID");
369 		return FALSE;
370 	}
371 
372 	/* Certificate.HashTypeGuid MUST be equal to A7717414-C616-4977-9420-844712A735BF */
373 	if (0 != memcmp(&certificate->hash_type_guid, &gEfiSignCertTypeRsa2048Sha256Guid, sizeof(struct efi_guid_t))) {
374 		AUTHDBG("invalid hash type GUID");
375 		return FALSE;
376 	}
377 
378 	return TRUE;
379 }
380 
381 static int
validate_rev2_chunklist(uint8_t * buffer,size_t buffer_size)382 validate_rev2_chunklist(uint8_t *buffer, size_t buffer_size)
383 {
384 	struct rev2_chunklist_certificate *certificate;
385 	size_t security_data_offset;
386 
387 	/* Check input parameters to be sane */
388 	if (buffer == NULL || buffer_size == 0) {
389 		AUTHDBG("invalid parameter");
390 		return EINVAL;
391 	}
392 
393 	/* Check for existing signature */
394 	if (buffer_size < sizeof(struct rev2_chunklist_certificate)) {
395 		AUTHDBG("no space for certificate");
396 		return EINVAL;
397 	}
398 
399 	security_data_offset = buffer_size - sizeof(struct rev2_chunklist_certificate);
400 	certificate = (struct rev2_chunklist_certificate*)(buffer + security_data_offset);
401 
402 	/* Check signature candidate to be a valid rev2 chunklist certificate */
403 	if (TRUE != validate_rev2_certificate(certificate)) {
404 		return EINVAL;
405 	}
406 
407 	/* Check public key to be trusted */
408 	for (size_t i = 0; i < rev2_chunklist_num_pubkeys; i++) {
409 		const struct chunklist_pubkey *key = &rev2_chunklist_pubkeys[i];
410 		/* Production keys are always trusted */
411 		if (key->is_production != TRUE) {
412 			uint8_t no_rev2_dev = 0;
413 			/* Do not trust rev2 development keys if CHUNKLIST_NO_REV2_DEV is present */
414 			if (PE_parse_boot_argn(CHUNKLIST_NO_REV2_DEV, &no_rev2_dev, sizeof(no_rev2_dev))) {
415 				AUTHDBG("rev2 development key is not trusted");
416 				continue;
417 			}
418 		}
419 
420 		/* Check certificate public key to be the trusted one */
421 		if (0 == memcmp(key->key, certificate->rsa_public_key, sizeof(certificate->rsa_public_key))) {
422 			AUTHDBG("certificate public key is trusted");
423 
424 			/* Hash everything but signature */
425 			SHA256_CTX hash_ctx;
426 			SHA256_Init(&hash_ctx);
427 			SHA256_Update(&hash_ctx, buffer, security_data_offset);
428 
429 			/* Include Certificate.SecurityEpoch value */
430 			SHA256_Update(&hash_ctx, &certificate->security_epoch, sizeof(certificate->security_epoch));
431 
432 			/* Finalize hashing into the output buffer */
433 			uint8_t sha_digest[SHA256_DIGEST_LENGTH];
434 			SHA256_Final(sha_digest, &hash_ctx);
435 
436 			/* Validate signature */
437 			return validate_signature(certificate->rsa_public_key,
438 			           sizeof(certificate->rsa_public_key),
439 			           certificate->rsa_signature,
440 			           sizeof(certificate->rsa_signature),
441 			           sha_digest);
442 		}
443 	}
444 
445 	AUTHDBG("certificate public key is not trusted");
446 	return EINVAL;
447 }
448 
449 /*
450  * Main chunklist validation routine
451  */
452 static int
validate_chunklist(void * buf,size_t len)453 validate_chunklist(void *buf, size_t len)
454 {
455 	int err = 0;
456 	size_t sigsz = 0;
457 	size_t sig_end = 0;
458 	size_t chunks_end = 0;
459 	size_t sig_len = 0;
460 	boolean_t valid_sig = FALSE;
461 	struct chunklist_hdr *hdr = buf;
462 
463 	if (len < sizeof(struct chunklist_hdr)) {
464 		AUTHPRNT("no space for header");
465 		return EINVAL;
466 	}
467 
468 	/* recognized file format? */
469 	if (hdr->cl_magic != CHUNKLIST_MAGIC ||
470 	    hdr->cl_file_ver != CHUNKLIST_FILE_VERSION_10 ||
471 	    hdr->cl_chunk_method != CHUNKLIST_CHUNK_METHOD_10) {
472 		AUTHPRNT("unrecognized chunklist format");
473 		return EINVAL;
474 	}
475 
476 	/* determine signature length based on signature method */
477 	if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV1) {
478 		AUTHPRNT("rev1 chunklist");
479 		sig_len = CHUNKLIST_REV1_SIG_LEN;
480 	} else if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV2) {
481 		AUTHPRNT("rev2 chunklist");
482 		sig_len = CHUNKLIST_REV2_SIG_LEN;
483 	} else {
484 		AUTHPRNT("unrecognized chunklist signature method");
485 		return EINVAL;
486 	}
487 
488 	/* does the chunk list fall within the bounds of the buffer? */
489 	if (os_mul_and_add_overflow(hdr->cl_chunk_count, sizeof(struct chunklist_chunk), hdr->cl_chunk_offset, &chunks_end) ||
490 	    hdr->cl_chunk_offset < sizeof(struct chunklist_hdr) || chunks_end > len) {
491 		AUTHPRNT("invalid chunk_count (%llu) or chunk_offset (%llu)",
492 		    hdr->cl_chunk_count, hdr->cl_chunk_offset);
493 		return EINVAL;
494 	}
495 
496 	/* does the signature fall within the bounds of the buffer? */
497 	if (os_add_overflow(hdr->cl_sig_offset, sig_len, &sig_end) ||
498 	    hdr->cl_sig_offset < sizeof(struct chunklist_hdr) ||
499 	    hdr->cl_sig_offset < chunks_end ||
500 	    hdr->cl_sig_offset > len) {
501 		AUTHPRNT("invalid signature offset (%llu)", hdr->cl_sig_offset);
502 		return EINVAL;
503 	}
504 
505 	if (sig_end > len ||
506 	    os_sub_overflow(len, hdr->cl_sig_offset, &sigsz) ||
507 	    sigsz != sig_len) {
508 		/* missing or incorrect signature size */
509 		return EINVAL;
510 	}
511 
512 	/* validate rev1 chunklist */
513 	if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV1) {
514 		/* Do not trust rev1 chunklists if CHUNKLIST_NO_REV1 is present */
515 		uint8_t no_rev1;
516 		if (PE_parse_boot_argn(CHUNKLIST_NO_REV1, &no_rev1, sizeof(no_rev1))) {
517 			AUTHDBG("rev1 chunklists are not trusted");
518 			return EINVAL;
519 		}
520 
521 		/* hash the chunklist (excluding the signature) */
522 		AUTHDBG("hashing rev1 chunklist");
523 		uint8_t sha_digest[SHA256_DIGEST_LENGTH];
524 		SHA256_CTX sha_ctx;
525 		SHA256_Init(&sha_ctx);
526 		SHA256_Update(&sha_ctx, buf, hdr->cl_sig_offset);
527 		SHA256_Final(sha_digest, &sha_ctx);
528 
529 		AUTHDBG("validating rev1 chunklist signature against rev1 pub keys");
530 		for (size_t i = 0; i < rev1_chunklist_num_pubkeys; i++) {
531 			const struct chunklist_pubkey *key = &rev1_chunklist_pubkeys[i];
532 			err = validate_signature(key->key, CHUNKLIST_PUBKEY_LEN, (uint8_t *)((uintptr_t)buf + hdr->cl_sig_offset),
533 			    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
authenticate_root_with_chunklist(const char * rootdmg_path,boolean_t * out_enforced)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(chunklist_path, &chunklist_buf, &chunklist_len, NULL);
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 	kfree_data(chunklist_buf, chunklist_len);
633 	zfree(ZV_NAMEI, chunklist_path);
634 	return err;
635 }
636 
637 int
authenticate_root_version_check(void)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
authenticate_bootkc_uuid(void)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(bootkc_path, &buf, &bufsz, NULL);
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 	kfree_data(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
authenticate_libkern_uuid(void)702 authenticate_libkern_uuid(void)
703 {
704 	int err = 0;
705 	void *buf = NULL;
706 	size_t bufsz = 4 * 1024 * 1024UL;
707 	off_t fsize = 0;
708 
709 	/* get the UUID of the libkern in /S/L/E */
710 	err = imageboot_read_file(libkern_path, &buf, &bufsz, &fsize);
711 	if (err) {
712 		goto out;
713 	}
714 
715 	if (fatfile_validate_fatarches((vm_offset_t)buf, bufsz, fsize) == LOAD_SUCCESS) {
716 		struct fat_header *fat_header = buf;
717 		struct fat_arch fat_arch;
718 		if (fatfile_getbestarch((vm_offset_t)fat_header, bufsz, NULL, &fat_arch, FALSE) != LOAD_SUCCESS) {
719 			err = EINVAL;
720 			goto out;
721 		}
722 		kfree_data(buf, bufsz);
723 		buf = NULL;
724 		bufsz = MIN(fat_arch.size, 4 * 1024 * 1024UL);
725 		err = imageboot_read_file_from_offset(libkern_path, fat_arch.offset, &buf, &bufsz);
726 		if (err) {
727 			goto out;
728 		}
729 	}
730 
731 	unsigned long uuidsz = 0;
732 	const uuid_t *img_uuid = getuuidfromheader_safe(buf, bufsz, &uuidsz);
733 	if (img_uuid == NULL || uuidsz != sizeof(uuid_t)) {
734 		AUTHPRNT("invalid UUID (sz = %lu)", uuidsz);
735 		err = EINVAL;
736 		goto out;
737 	}
738 
739 	/* Get the UUID of the loaded libkern */
740 	uuid_t live_uuid;
741 	err = OSKextGetUUIDForName(libkern_bundle, live_uuid);
742 	if (err) {
743 		AUTHPRNT("could not find loaded libkern");
744 		goto out;
745 	}
746 
747 	/* ... and compare them */
748 	if (bcmp(live_uuid, img_uuid, uuidsz) != 0) {
749 		AUTHPRNT("UUID of running libkern does not match %s", libkern_path);
750 
751 		uuid_string_t img_uuid_str, live_uuid_str;
752 		uuid_unparse(*img_uuid, img_uuid_str);
753 		uuid_unparse(live_uuid, live_uuid_str);
754 		AUTHPRNT("loaded libkern UUID =  %s", live_uuid_str);
755 		AUTHPRNT("on-disk libkern UUID = %s", img_uuid_str);
756 
757 		err = EINVAL;
758 		goto out;
759 	}
760 
761 	/* UUID matches! */
762 out:
763 	kfree_data(buf, bufsz);
764 	return err;
765 }
766