1/*
2 * Copyright (c) 2022 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <os/overflow.h>
24#include <machine/atomic.h>
25#include <mach/vm_param.h>
26#include <vm/vm_kern.h>
27#include <kern/zalloc.h>
28#include <kern/kalloc.h>
29#include <kern/assert.h>
30#include <kern/locks.h>
31#include <kern/lock_rw.h>
32#include <libkern/libkern.h>
33#include <libkern/section_keywords.h>
34#include <libkern/coretrust/coretrust.h>
35#include <pexpert/pexpert.h>
36#include <sys/vm.h>
37#include <sys/proc.h>
38#include <sys/codesign.h>
39#include <sys/code_signing.h>
40#include <uuid/uuid.h>
41#include <IOKit/IOBSD.h>
42
43#if PMAP_CS_PPL_MONITOR
44/*
45 * The Page Protection Layer layer implements the PMAP_CS monitor environment which
46 * provides code signing and memory isolation enforcements for data structures which
47 * are critical to ensuring that all code executed on the system is authorized to do
48 * so.
49 *
50 * Unless the data is managed by the PPL itself, XNU needs to page-align everything,
51 * and then reference the memory as read-only.
52 */
53
54typedef uint64_t pmap_paddr_t __kernel_ptr_semantics;
55extern vm_map_address_t phystokv(pmap_paddr_t pa);
56extern pmap_paddr_t kvtophys_nofail(vm_offset_t va);
57
58#pragma mark Initialization
59
60void
61code_signing_init()
62{
63 /* Does nothing */
64}
65
66void
67ppl_enter_lockdown_mode(void)
68{
69 /*
70 * This function is expected to be called before read-only lockdown on the
71 * system. As a result, the PPL variable should be mutable. If not, then we
72 * will panic (as we should).
73 */
74 ppl_lockdown_mode_enabled = true;
75
76 printf("entered lockdown mode policy for the PPL");
77}
78
79#pragma mark Developer Mode
80
81SECURITY_READ_ONLY_LATE(bool*) developer_mode_enabled = &ppl_developer_mode_storage;
82
83void
84ppl_toggle_developer_mode(
85 bool state)
86{
87 pmap_toggle_developer_mode(state);
88}
89
90#pragma mark Code Signing and Provisioning Profiles
91
92bool
93ppl_code_signing_enabled(void)
94{
95 return pmap_cs_enabled();
96}
97
98kern_return_t
99ppl_register_provisioning_profile(
100 const void *profile_blob,
101 const size_t profile_blob_size,
102 void **profile_obj)
103{
104 pmap_profile_payload_t *pmap_payload = NULL;
105 vm_address_t payload_addr = 0;
106 vm_size_t payload_size = 0;
107 vm_size_t payload_size_aligned = 0;
108 kern_return_t ret = KERN_DENIED;
109
110 if (os_add_overflow(sizeof(*pmap_payload), profile_blob_size, &payload_size)) {
111 panic("attempted to load a too-large profile: %lu bytes", profile_blob_size);
112 }
113 payload_size_aligned = round_page(payload_size);
114
115 ret = kmem_alloc(kernel_map, &payload_addr, payload_size_aligned,
116 KMA_KOBJECT | KMA_DATA | KMA_ZERO, VM_KERN_MEMORY_SECURITY);
117 if (ret != KERN_SUCCESS) {
118 printf("unable to allocate memory for pmap profile payload: %d\n", ret);
119 goto exit;
120 }
121
122 /* We need to setup the payload before we send it to the PPL */
123 pmap_payload = (pmap_profile_payload_t*)payload_addr;
124
125 pmap_payload->profile_blob_size = profile_blob_size;
126 memcpy(pmap_payload->profile_blob, profile_blob, profile_blob_size);
127
128 ret = pmap_register_provisioning_profile(payload_addr, payload_size_aligned);
129 if (ret == KERN_SUCCESS) {
130 *profile_obj = &pmap_payload->profile_obj_storage;
131 *profile_obj = (pmap_cs_profile_t*)phystokv(kvtophys_nofail((vm_offset_t)*profile_obj));
132 }
133
134exit:
135 if ((ret != KERN_SUCCESS) && (payload_addr != 0)) {
136 kmem_free(kernel_map, payload_addr, payload_size_aligned);
137 payload_addr = 0;
138 payload_size_aligned = 0;
139 }
140
141 return ret;
142}
143
144kern_return_t
145ppl_unregister_provisioning_profile(
146 void *profile_obj)
147{
148 pmap_cs_profile_t *ppl_profile_obj = profile_obj;
149 kern_return_t ret = KERN_DENIED;
150
151 ret = pmap_unregister_provisioning_profile(ppl_profile_obj);
152 if (ret != KERN_SUCCESS) {
153 return ret;
154 }
155
156 /* Get the original payload address */
157 const pmap_profile_payload_t *pmap_payload = ppl_profile_obj->original_payload;
158 const vm_address_t payload_addr = (const vm_address_t)pmap_payload;
159
160 /* Get the original payload size */
161 vm_size_t payload_size = pmap_payload->profile_blob_size + sizeof(*pmap_payload);
162 payload_size = round_page(payload_size);
163
164 /* Free the payload */
165 kmem_free(kernel_map, payload_addr, payload_size);
166 pmap_payload = NULL;
167
168 return KERN_SUCCESS;
169}
170
171kern_return_t
172ppl_associate_provisioning_profile(
173 void *sig_obj,
174 void *profile_obj)
175{
176 return pmap_associate_provisioning_profile(sig_obj, profile_obj);
177}
178
179kern_return_t
180ppl_disassociate_provisioning_profile(
181 void *sig_obj)
182{
183 return pmap_disassociate_provisioning_profile(sig_obj);
184}
185
186void
187ppl_set_compilation_service_cdhash(
188 const uint8_t cdhash[CS_CDHASH_LEN])
189{
190 pmap_set_compilation_service_cdhash(cdhash);
191}
192
193bool
194ppl_match_compilation_service_cdhash(
195 const uint8_t cdhash[CS_CDHASH_LEN])
196{
197 return pmap_match_compilation_service_cdhash(cdhash);
198}
199
200void
201ppl_set_local_signing_public_key(
202 const uint8_t public_key[XNU_LOCAL_SIGNING_KEY_SIZE])
203{
204 return pmap_set_local_signing_public_key(public_key);
205}
206
207uint8_t*
208ppl_get_local_signing_public_key(void)
209{
210 return pmap_get_local_signing_public_key();
211}
212
213void
214ppl_unrestrict_local_signing_cdhash(
215 const uint8_t cdhash[CS_CDHASH_LEN])
216{
217 pmap_unrestrict_local_signing(cdhash);
218}
219
220vm_size_t
221ppl_managed_code_signature_size(void)
222{
223 return pmap_cs_blob_limit;
224}
225
226kern_return_t
227ppl_register_code_signature(
228 const vm_address_t signature_addr,
229 const vm_size_t signature_size,
230 const vm_offset_t code_directory_offset,
231 const char *signature_path,
232 void **sig_obj,
233 vm_address_t *ppl_signature_addr)
234{
235 pmap_cs_code_directory_t *cd_entry = NULL;
236
237 /* PPL doesn't care about the signature path */
238 (void)signature_path;
239
240 kern_return_t ret = pmap_cs_register_code_signature_blob(
241 signature_addr,
242 signature_size,
243 code_directory_offset,
244 (pmap_cs_code_directory_t**)sig_obj);
245
246 if (ret != KERN_SUCCESS) {
247 return ret;
248 }
249 cd_entry = *((pmap_cs_code_directory_t**)sig_obj);
250
251 if (ppl_signature_addr) {
252 *ppl_signature_addr = (vm_address_t)cd_entry->superblob;
253 }
254
255 return KERN_SUCCESS;
256}
257
258kern_return_t
259ppl_unregister_code_signature(
260 void *sig_obj)
261{
262 return pmap_cs_unregister_code_signature_blob(sig_obj);
263}
264
265kern_return_t
266ppl_verify_code_signature(
267 void *sig_obj)
268{
269 return pmap_cs_verify_code_signature_blob(sig_obj);
270}
271
272kern_return_t
273ppl_reconstitute_code_signature(
274 void *sig_obj,
275 vm_address_t *unneeded_addr,
276 vm_size_t *unneeded_size)
277{
278 return pmap_cs_unlock_unneeded_code_signature(
279 sig_obj,
280 unneeded_addr,
281 unneeded_size);
282}
283
284#pragma mark Address Spaces
285
286kern_return_t
287ppl_associate_code_signature(
288 pmap_t pmap,
289 void *sig_obj,
290 const vm_address_t region_addr,
291 const vm_size_t region_size,
292 const vm_offset_t region_offset)
293{
294 return pmap_cs_associate(
295 pmap,
296 sig_obj,
297 region_addr,
298 region_size,
299 region_offset);
300}
301
302kern_return_t
303ppl_allow_jit_region(
304 __unused pmap_t pmap)
305{
306 /* PPL does not support this API */
307 return KERN_NOT_SUPPORTED;
308}
309
310kern_return_t
311ppl_associate_jit_region(
312 pmap_t pmap,
313 const vm_address_t region_addr,
314 const vm_size_t region_size)
315{
316 return pmap_cs_associate(
317 pmap,
318 PMAP_CS_ASSOCIATE_JIT,
319 region_addr,
320 region_size,
321 0);
322}
323
324kern_return_t
325ppl_associate_debug_region(
326 pmap_t pmap,
327 const vm_address_t region_addr,
328 const vm_size_t region_size)
329{
330 return pmap_cs_associate(
331 pmap,
332 PMAP_CS_ASSOCIATE_COW,
333 region_addr,
334 region_size,
335 0);
336}
337
338kern_return_t
339ppl_address_space_debugged(
340 pmap_t pmap)
341{
342 /*
343 * ppl_associate_debug_region is a fairly idempotent function which simply
344 * checks if an address space is already debugged or not and returns a value
345 * based on that. The actual memory region is not inserted into the address
346 * space, so we can pass whatever in this case. The only caveat here though
347 * is that the memory region needs to be page-aligned and cannot be NULL.
348 */
349 return ppl_associate_debug_region(pmap, PAGE_SIZE, PAGE_SIZE);
350}
351
352kern_return_t
353ppl_allow_invalid_code(
354 pmap_t pmap)
355{
356 return pmap_cs_allow_invalid(pmap);
357}
358
359kern_return_t
360ppl_get_trust_level_kdp(
361 pmap_t pmap,
362 uint32_t *trust_level)
363{
364 return pmap_get_trust_level_kdp(pmap, trust_level);
365}
366
367kern_return_t
368ppl_address_space_exempt(
369 const pmap_t pmap)
370{
371 if (pmap_performs_stage2_translations(pmap) == true) {
372 return KERN_SUCCESS;
373 }
374
375 return KERN_DENIED;
376}
377
378kern_return_t
379ppl_fork_prepare(
380 pmap_t old_pmap,
381 pmap_t new_pmap)
382{
383 return pmap_cs_fork_prepare(old_pmap, new_pmap);
384}
385
386kern_return_t
387ppl_acquire_signing_identifier(
388 const void *sig_obj,
389 const char **signing_id)
390{
391 const pmap_cs_code_directory_t *cd_entry = sig_obj;
392
393 /* If we reach here, the identifier must have been setup */
394 assert(cd_entry->identifier != NULL);
395
396 if (signing_id) {
397 *signing_id = cd_entry->identifier;
398 }
399
400 return KERN_SUCCESS;
401}
402
403#pragma mark Entitlements
404
405kern_return_t
406ppl_associate_kernel_entitlements(
407 void *sig_obj,
408 const void *kernel_entitlements)
409{
410 pmap_cs_code_directory_t *cd_entry = sig_obj;
411 return pmap_associate_kernel_entitlements(cd_entry, kernel_entitlements);
412}
413
414kern_return_t
415ppl_resolve_kernel_entitlements(
416 pmap_t pmap,
417 const void **kernel_entitlements)
418{
419 kern_return_t ret = KERN_DENIED;
420 const void *entitlements = NULL;
421
422 ret = pmap_resolve_kernel_entitlements(pmap, &entitlements);
423 if ((ret == KERN_SUCCESS) && (kernel_entitlements != NULL)) {
424 *kernel_entitlements = entitlements;
425 }
426
427 return ret;
428}
429
430kern_return_t
431ppl_accelerate_entitlements(
432 void *sig_obj,
433 CEQueryContext_t *ce_ctx)
434{
435 pmap_cs_code_directory_t *cd_entry = sig_obj;
436 kern_return_t ret = KERN_DENIED;
437
438 ret = pmap_accelerate_entitlements(cd_entry);
439
440 /*
441 * We only ever get KERN_ABORTED when we cannot accelerate the entitlements
442 * because it would consume too much memory. In this case, we still want to
443 * return the ce_ctx since we don't want the system to fall-back to non-PPL
444 * locked down memory, so we switch this to a success case.
445 */
446 if (ret == KERN_ABORTED) {
447 ret = KERN_SUCCESS;
448 }
449
450 /* Return the accelerated context to the caller */
451 if ((ret == KERN_SUCCESS) && (ce_ctx != NULL)) {
452 *ce_ctx = cd_entry->ce_ctx;
453 }
454
455 return ret;
456}
457
458#pragma mark Image4
459
460void*
461ppl_image4_storage_data(
462 size_t *allocated_size)
463{
464 return pmap_image4_pmap_data(allocated_size);
465}
466
467void
468ppl_image4_set_nonce(
469 const img4_nonce_domain_index_t ndi,
470 const img4_nonce_t *nonce)
471{
472 return pmap_image4_set_nonce(ndi, nonce);
473}
474
475void
476ppl_image4_roll_nonce(
477 const img4_nonce_domain_index_t ndi)
478{
479 return pmap_image4_roll_nonce(ndi);
480}
481
482errno_t
483ppl_image4_copy_nonce(
484 const img4_nonce_domain_index_t ndi,
485 img4_nonce_t *nonce_out)
486{
487 return pmap_image4_copy_nonce(ndi, nonce_out);
488}
489
490errno_t
491ppl_image4_execute_object(
492 img4_runtime_object_spec_index_t obj_spec_index,
493 const img4_buff_t *payload,
494 const img4_buff_t *manifest)
495{
496 errno_t err = EINVAL;
497 kern_return_t kr = KERN_DENIED;
498 img4_buff_t payload_aligned = IMG4_BUFF_INIT;
499 img4_buff_t manifest_aligned = IMG4_BUFF_INIT;
500 vm_address_t payload_addr = 0;
501 vm_size_t payload_len_aligned = 0;
502 vm_address_t manifest_addr = 0;
503 vm_size_t manifest_len_aligned = 0;
504
505 if (payload == NULL) {
506 printf("invalid object execution request: no payload\n");
507 goto out;
508 }
509
510 /*
511 * The PPL will attempt to lockdown both the payload and the manifest before executing
512 * the object. In order for that to happen, both the artifacts need to be page-aligned.
513 */
514 payload_len_aligned = round_page(payload->i4b_len);
515 if (manifest != NULL) {
516 manifest_len_aligned = round_page(manifest->i4b_len);
517 }
518
519 kr = kmem_alloc(
520 kernel_map,
521 &payload_addr,
522 payload_len_aligned,
523 KMA_KOBJECT,
524 VM_KERN_MEMORY_SECURITY);
525
526 if (kr != KERN_SUCCESS) {
527 printf("unable to allocate memory for image4 payload: %d\n", kr);
528 err = ENOMEM;
529 goto out;
530 }
531
532 /* Copy in the payload */
533 memcpy((uint8_t*)payload_addr, payload->i4b_bytes, payload->i4b_len);
534
535 /* Construct the aligned payload buffer */
536 payload_aligned.i4b_bytes = (uint8_t*)payload_addr;
537 payload_aligned.i4b_len = payload->i4b_len;
538
539 if (manifest != NULL) {
540 kr = kmem_alloc(
541 kernel_map,
542 &manifest_addr,
543 manifest_len_aligned,
544 KMA_KOBJECT,
545 VM_KERN_MEMORY_SECURITY);
546
547 if (kr != KERN_SUCCESS) {
548 printf("unable to allocate memory for image4 manifest: %d\n", kr);
549 err = ENOMEM;
550 goto out;
551 }
552
553 /* Construct the aligned manifest buffer */
554 manifest_aligned.i4b_bytes = (uint8_t*)manifest_addr;
555 manifest_aligned.i4b_len = manifest->i4b_len;
556
557 /* Copy in the manifest */
558 memcpy((uint8_t*)manifest_addr, manifest->i4b_bytes, manifest->i4b_len);
559 }
560
561 err = pmap_image4_execute_object(obj_spec_index, &payload_aligned, &manifest_aligned);
562 if (err != 0) {
563 printf("unable to execute image4 object: %d\n", err);
564 goto out;
565 }
566
567out:
568 /* We always free the manifest as it isn't required anymore */
569 if (manifest_addr != 0) {
570 kmem_free(kernel_map, manifest_addr, manifest_len_aligned);
571 manifest_addr = 0;
572 manifest_len_aligned = 0;
573 }
574
575 /* If we encountered an error -- free the allocated payload */
576 if ((err != 0) && (payload_addr != 0)) {
577 kmem_free(kernel_map, payload_addr, payload_len_aligned);
578 payload_addr = 0;
579 payload_len_aligned = 0;
580 }
581
582 return err;
583}
584
585errno_t
586ppl_image4_copy_object(
587 img4_runtime_object_spec_index_t obj_spec_index,
588 vm_address_t object_out,
589 size_t *object_length)
590{
591 errno_t err = EINVAL;
592 kern_return_t kr = KERN_DENIED;
593 vm_address_t object_addr = 0;
594 vm_size_t object_len_aligned = 0;
595
596 if (object_out == 0) {
597 printf("invalid object copy request: no object input buffer\n");
598 goto out;
599 } else if (object_length == NULL) {
600 printf("invalid object copy request: no object input length\n");
601 goto out;
602 }
603
604 /*
605 * The PPL will attempt to pin the input buffer in order to ensure that the kernel
606 * didn't pass in PPL-owned buffers. The PPL cannot pin the same page more than once,
607 * and attempting to do so will panic the system. Hence, we allocate fresh pages for
608 * for the PPL to pin.
609 *
610 * We can send in the address for the length pointer since that is allocated on the
611 * stack, so the PPL can pin our stack for the duration of the call as no other
612 * thread can be using our stack, meaning the PPL will never attempt to double-pin
613 * the page.
614 */
615 object_len_aligned = round_page(*object_length);
616
617 kr = kmem_alloc(
618 kernel_map,
619 &object_addr,
620 object_len_aligned,
621 KMA_KOBJECT,
622 VM_KERN_MEMORY_SECURITY);
623
624 if (kr != KERN_SUCCESS) {
625 printf("unable to allocate memory for image4 object: %d\n", kr);
626 err = ENOMEM;
627 goto out;
628 }
629
630 err = pmap_image4_copy_object(obj_spec_index, object_addr, object_length);
631 if (err != 0) {
632 printf("unable to copy image4 object: %d\n", err);
633 goto out;
634 }
635
636 /* Copy the data back into the caller passed buffer */
637 memcpy((void*)object_out, (void*)object_addr, *object_length);
638
639out:
640 /* We don't ever need to keep around our page-aligned buffer */
641 if (object_addr != 0) {
642 kmem_free(kernel_map, object_addr, object_len_aligned);
643 object_addr = 0;
644 object_len_aligned = 0;
645 }
646
647 return err;
648}
649
650const void*
651ppl_image4_get_monitor_exports(void)
652{
653 /*
654 * AppleImage4 can query the PMAP_CS runtime on its own since the PMAP_CS
655 * runtime is compiled within the kernel extension itself. As a result, we
656 * never expect this KPI to be called when the system uses the PPL monitor.
657 */
658
659 printf("explicit monitor-exports-get not required for the PPL\n");
660 return NULL;
661}
662
663errno_t
664ppl_image4_set_release_type(
665 __unused const char *release_type)
666{
667 /*
668 * AppleImage4 stores the release type in the CTRR protected memory region
669 * of its kernel extension. This is accessible by the PMAP_CS runtime as the
670 * runtime is compiled alongside the kernel extension. As a result, we never
671 * expect this KPI to be called when the system uses the PPL monitor.
672 */
673
674 printf("explicit release-type-set set not required for the PPL\n");
675 return ENOTSUP;
676}
677
678errno_t
679ppl_image4_set_bnch_shadow(
680 __unused const img4_nonce_domain_index_t ndi)
681{
682 /*
683 * AppleImage4 stores the BNCH shadow in the CTRR protected memory region
684 * of its kernel extension. This is accessible by the PMAP_CS runtime as the
685 * runtime is compiled alongside the kernel extension. As a result, we never
686 * expect this KPI to be called when the system uses the PPL monitor.
687 */
688
689 printf("explicit BNCH-shadow-set not required for the PPL\n");
690 return ENOTSUP;
691}
692
693#pragma mark Image4 - New
694
695kern_return_t
696ppl_image4_transfer_region(
697 __unused image4_cs_trap_t selector,
698 __unused vm_address_t region_addr,
699 __unused vm_size_t region_size)
700{
701 /* All regions transfers happen internally with the PPL */
702 return KERN_SUCCESS;
703}
704
705kern_return_t
706ppl_image4_reclaim_region(
707 __unused image4_cs_trap_t selector,
708 __unused vm_address_t region_addr,
709 __unused vm_size_t region_size)
710{
711 /* All regions transfers happen internally with the PPL */
712 return KERN_SUCCESS;
713}
714
715errno_t
716ppl_image4_monitor_trap(
717 image4_cs_trap_t selector,
718 const void *input_data,
719 size_t input_size)
720{
721 return pmap_image4_monitor_trap(selector, input_data, input_size);
722}
723
724#endif /* PMAP_CS_PPL_MONITOR */
725