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 * Main chunklist validation routine
327 */
328 static int
validate_chunklist(void * buf,size_t len)329 validate_chunklist(void *buf, size_t len)
330 {
331 int err = 0;
332 size_t sigsz = 0;
333 size_t sig_end = 0;
334 size_t chunks_end = 0;
335 size_t sig_len = 0;
336 boolean_t valid_sig = FALSE;
337 struct chunklist_hdr *hdr = buf;
338
339 if (len < sizeof(struct chunklist_hdr)) {
340 AUTHPRNT("no space for header");
341 return EINVAL;
342 }
343
344 /* recognized file format? */
345 if (hdr->cl_magic != CHUNKLIST_MAGIC ||
346 hdr->cl_file_ver != CHUNKLIST_FILE_VERSION_10 ||
347 hdr->cl_chunk_method != CHUNKLIST_CHUNK_METHOD_10) {
348 AUTHPRNT("unrecognized chunklist format");
349 return EINVAL;
350 }
351
352 /* determine signature length based on signature method */
353 if (hdr->cl_sig_method == CHUNKLIST_SIGNATURE_METHOD_REV1) {
354 AUTHPRNT("rev1 chunklist");
355 sig_len = CHUNKLIST_REV1_SIG_LEN;
356 } else {
357 AUTHPRNT("unrecognized chunklist signature method");
358 return EINVAL;
359 }
360
361 /* does the chunk list fall within the bounds of the buffer? */
362 if (os_mul_and_add_overflow(hdr->cl_chunk_count, sizeof(struct chunklist_chunk), hdr->cl_chunk_offset, &chunks_end) ||
363 hdr->cl_chunk_offset < sizeof(struct chunklist_hdr) || chunks_end > len) {
364 AUTHPRNT("invalid chunk_count (%llu) or chunk_offset (%llu)",
365 hdr->cl_chunk_count, hdr->cl_chunk_offset);
366 return EINVAL;
367 }
368
369 /* does the signature fall within the bounds of the buffer? */
370 if (os_add_overflow(hdr->cl_sig_offset, sig_len, &sig_end) ||
371 hdr->cl_sig_offset < sizeof(struct chunklist_hdr) ||
372 hdr->cl_sig_offset < chunks_end ||
373 hdr->cl_sig_offset > len) {
374 AUTHPRNT("invalid signature offset (%llu)", hdr->cl_sig_offset);
375 return EINVAL;
376 }
377
378 if (sig_end > len ||
379 os_sub_overflow(len, hdr->cl_sig_offset, &sigsz) ||
380 sigsz != sig_len) {
381 /* missing or incorrect signature size */
382 return EINVAL;
383 }
384
385 /* validate rev1 chunklist */
386 /* hash the chunklist (excluding the signature) */
387 AUTHDBG("hashing rev1 chunklist");
388 uint8_t sha_digest[SHA256_DIGEST_LENGTH];
389 SHA256_CTX sha_ctx;
390 SHA256_Init(&sha_ctx);
391 SHA256_Update(&sha_ctx, buf, hdr->cl_sig_offset);
392 SHA256_Final(sha_digest, &sha_ctx);
393
394 AUTHDBG("validating rev1 chunklist signature against rev1 pub keys");
395 for (size_t i = 0; i < rev1_chunklist_num_pubkeys; i++) {
396 const struct chunklist_pubkey *key = &rev1_chunklist_pubkeys[i];
397 err = validate_signature(key->key, CHUNKLIST_PUBKEY_LEN, (uint8_t *)((uintptr_t)buf + hdr->cl_sig_offset),
398 CHUNKLIST_SIGNATURE_LEN, sha_digest);
399 if (err == 0) {
400 AUTHDBG("validated rev1 chunklist signature with rev1 key %lu (prod=%d)", i, key->is_production);
401 valid_sig = key->is_production;
402 #if IMAGEBOOT_ALLOW_DEVKEYS
403 if (!key->is_production) {
404 /* allow dev keys in dev builds only */
405 AUTHDBG("*** allowing DEV rev1 key: this will fail in customer builds ***");
406 valid_sig = TRUE;
407 }
408 #endif
409 goto out;
410 }
411 }
412
413 /* At this point we tried all the keys: nothing went wrong but none of them
414 * signed our chunklist. */
415 AUTHPRNT("rev1 signature did not verify against any known rev1 public key");
416
417 out:
418 if (err) {
419 return err;
420 } else if (valid_sig == TRUE) {
421 return 0; /* signed, and everything checked out */
422 } else {
423 return EINVAL;
424 }
425 }
426
427 /*
428 * Authenticate a given DMG file using chunklist
429 */
430 int
authenticate_root_with_chunklist(const char * rootdmg_path,boolean_t * out_enforced)431 authenticate_root_with_chunklist(const char *rootdmg_path, boolean_t *out_enforced)
432 {
433 char *chunklist_path = NULL;
434 void *chunklist_buf = NULL;
435 size_t chunklist_len = 32 * 1024 * 1024UL;
436 boolean_t enforced = TRUE;
437 int err = 0;
438
439 chunklist_path = zalloc(ZV_NAMEI);
440 err = construct_chunklist_path(chunklist_path, rootdmg_path);
441 if (err) {
442 AUTHPRNT("failed creating chunklist path");
443 goto out;
444 }
445
446 AUTHDBG("validating root against chunklist %s", chunklist_path);
447
448 /*
449 * Read and authenticate the chunklist, then validate the root image against
450 * the chunklist.
451 */
452 AUTHDBG("reading chunklist");
453 err = imageboot_read_file(chunklist_path, &chunklist_buf, &chunklist_len, NULL);
454 if (err) {
455 AUTHPRNT("failed to read chunklist");
456 goto out;
457 }
458
459 AUTHDBG("validating chunklist");
460 err = validate_chunklist(chunklist_buf, chunklist_len);
461 if (err) {
462 AUTHPRNT("failed to validate chunklist");
463 goto out;
464 }
465 AUTHDBG("successfully validated chunklist");
466
467 AUTHDBG("validating root image against chunklist");
468 err = validate_root_image(rootdmg_path, chunklist_buf);
469 if (err) {
470 AUTHPRNT("failed to validate root image against chunklist (%d)", err);
471 goto out;
472 }
473
474 /* everything checked out - go ahead and mount this */
475 AUTHDBG("root image authenticated");
476
477 out:
478 #if CONFIG_CSR
479 if (err && (csr_check(CSR_ALLOW_ANY_RECOVERY_OS) == 0)) {
480 AUTHPRNT("CSR_ALLOW_ANY_RECOVERY_OS set, allowing unauthenticated root image");
481 err = 0;
482 enforced = FALSE;
483 }
484 #endif
485
486 if (out_enforced != NULL) {
487 *out_enforced = enforced;
488 }
489 kfree_data(chunklist_buf, chunklist_len);
490 zfree(ZV_NAMEI, chunklist_path);
491 return err;
492 }
493
494 int
authenticate_root_version_check(void)495 authenticate_root_version_check(void)
496 {
497 kc_format_t kc_format;
498 if (PE_get_primary_kc_format(&kc_format) && kc_format == KCFormatFileset) {
499 return authenticate_bootkc_uuid();
500 } else {
501 return authenticate_libkern_uuid();
502 }
503 }
504
505 /*
506 * Check that the UUID of the boot KC currently loaded matches the one on disk.
507 */
508 int
authenticate_bootkc_uuid(void)509 authenticate_bootkc_uuid(void)
510 {
511 int err = 0;
512 void *buf = NULL;
513 size_t bufsz = 1 * 1024 * 1024UL;
514
515 /* get the UUID of the bootkc in /S/L/KC */
516 err = imageboot_read_file(bootkc_path, &buf, &bufsz, NULL);
517 if (err) {
518 goto out;
519 }
520
521 unsigned long uuidsz = 0;
522 const uuid_t *img_uuid = getuuidfromheader_safe(buf, bufsz, &uuidsz);
523 if (img_uuid == NULL || uuidsz != sizeof(uuid_t)) {
524 AUTHPRNT("invalid UUID (sz = %lu)", uuidsz);
525 err = EINVAL;
526 goto out;
527 }
528
529 if (!kernelcache_uuid_valid) {
530 AUTHPRNT("Boot KC UUID was not set at boot.");
531 err = EINVAL;
532 goto out;
533 }
534
535 /* ... and compare them */
536 if (bcmp(&kernelcache_uuid, img_uuid, uuidsz) != 0) {
537 AUTHPRNT("UUID of running bootkc does not match %s", bootkc_path);
538
539 uuid_string_t img_uuid_str, live_uuid_str;
540 uuid_unparse(*img_uuid, img_uuid_str);
541 uuid_unparse(kernelcache_uuid, live_uuid_str);
542 AUTHPRNT("loaded bootkc UUID = %s", live_uuid_str);
543 AUTHPRNT("on-disk bootkc UUID = %s", img_uuid_str);
544
545 err = EINVAL;
546 goto out;
547 }
548
549 /* UUID matches! */
550 out:
551 kfree_data(buf, bufsz);
552 return err;
553 }
554
555 /*
556 * Check that the UUID of the libkern currently loaded matches the one on disk.
557 */
558 int
authenticate_libkern_uuid(void)559 authenticate_libkern_uuid(void)
560 {
561 int err = 0;
562 void *buf = NULL;
563 size_t bufsz = 4 * 1024 * 1024UL;
564 off_t fsize = 0;
565
566 /* get the UUID of the libkern in /S/L/E */
567 err = imageboot_read_file(libkern_path, &buf, &bufsz, &fsize);
568 if (err) {
569 goto out;
570 }
571
572 if (fatfile_validate_fatarches((vm_offset_t)buf, bufsz, fsize) == LOAD_SUCCESS) {
573 struct fat_header *fat_header = buf;
574 struct fat_arch fat_arch;
575 if (fatfile_getbestarch((vm_offset_t)fat_header, bufsz, NULL, &fat_arch, FALSE) != LOAD_SUCCESS) {
576 err = EINVAL;
577 goto out;
578 }
579 kfree_data(buf, bufsz);
580 buf = NULL;
581 bufsz = MIN(fat_arch.size, 4 * 1024 * 1024UL);
582 err = imageboot_read_file_from_offset(libkern_path, fat_arch.offset, &buf, &bufsz);
583 if (err) {
584 goto out;
585 }
586 }
587
588 unsigned long uuidsz = 0;
589 const uuid_t *img_uuid = getuuidfromheader_safe(buf, bufsz, &uuidsz);
590 if (img_uuid == NULL || uuidsz != sizeof(uuid_t)) {
591 AUTHPRNT("invalid UUID (sz = %lu)", uuidsz);
592 err = EINVAL;
593 goto out;
594 }
595
596 /* Get the UUID of the loaded libkern */
597 uuid_t live_uuid;
598 err = OSKextGetUUIDForName(libkern_bundle, live_uuid);
599 if (err) {
600 AUTHPRNT("could not find loaded libkern");
601 goto out;
602 }
603
604 /* ... and compare them */
605 if (bcmp(live_uuid, img_uuid, uuidsz) != 0) {
606 AUTHPRNT("UUID of running libkern does not match %s", libkern_path);
607
608 uuid_string_t img_uuid_str, live_uuid_str;
609 uuid_unparse(*img_uuid, img_uuid_str);
610 uuid_unparse(live_uuid, live_uuid_str);
611 AUTHPRNT("loaded libkern UUID = %s", live_uuid_str);
612 AUTHPRNT("on-disk libkern UUID = %s", img_uuid_str);
613
614 err = EINVAL;
615 goto out;
616 }
617
618 /* UUID matches! */
619 out:
620 kfree_data(buf, bufsz);
621 return err;
622 }
623