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 !CODE_SIGNING_MONITOR |
44 | /* |
45 | * We don't have a monitor environment available. This means someone with a kernel |
46 | * memory exploit will be able to corrupt code signing state. There is not much we |
47 | * can do here, since this is older HW. |
48 | */ |
49 | LCK_GRP_DECLARE(xnu_codesigning_lck_grp, "xnu_codesigning_lck_grp" ); |
50 | |
51 | #pragma mark Initialization |
52 | |
53 | static decl_lck_mtx_data(, compilation_service_lock); |
54 | |
55 | void |
56 | code_signing_init() |
57 | { |
58 | /* Initialize compilation service lock */ |
59 | lck_mtx_init(lck: &compilation_service_lock, grp: &xnu_codesigning_lck_grp, attr: 0); |
60 | } |
61 | |
62 | #pragma mark Developer Mode |
63 | |
64 | static bool developer_mode_storage = true; |
65 | SECURITY_READ_ONLY_LATE(bool*) developer_mode_enabled = &developer_mode_storage; |
66 | |
67 | void |
68 | xnu_toggle_developer_mode( |
69 | bool state) |
70 | { |
71 | /* No extra validation needed within XNU */ |
72 | os_atomic_store(developer_mode_enabled, state, relaxed); |
73 | } |
74 | |
75 | #pragma mark Code Signing |
76 | |
77 | static uint8_t compilation_service_cdhash[CS_CDHASH_LEN] = {0}; |
78 | |
79 | void |
80 | xnu_set_compilation_service_cdhash( |
81 | const uint8_t cdhash[CS_CDHASH_LEN]) |
82 | { |
83 | lck_mtx_lock(lck: &compilation_service_lock); |
84 | memcpy(dst: compilation_service_cdhash, src: cdhash, n: CS_CDHASH_LEN); |
85 | lck_mtx_unlock(lck: &compilation_service_lock); |
86 | } |
87 | |
88 | bool |
89 | xnu_match_compilation_service_cdhash( |
90 | const uint8_t cdhash[CS_CDHASH_LEN]) |
91 | { |
92 | bool match = false; |
93 | |
94 | lck_mtx_lock(lck: &compilation_service_lock); |
95 | if (bcmp(s1: compilation_service_cdhash, s2: cdhash, n: CS_CDHASH_LEN) == 0) { |
96 | match = true; |
97 | } |
98 | lck_mtx_unlock(lck: &compilation_service_lock); |
99 | |
100 | return match; |
101 | } |
102 | |
103 | static bool local_signing_key_set = false; |
104 | static uint8_t local_signing_public_key[XNU_LOCAL_SIGNING_KEY_SIZE] = {0}; |
105 | |
106 | void |
107 | xnu_set_local_signing_public_key( |
108 | const uint8_t public_key[XNU_LOCAL_SIGNING_KEY_SIZE]) |
109 | { |
110 | bool key_set = false; |
111 | |
112 | /* |
113 | * os_atomic_cmpxchg returns true in case the exchange was successful. For us, |
114 | * a successful exchange means that the local signing public key has _not_ been |
115 | * set. In case the key has been set, we panic as we would never expect the |
116 | * kernel to attempt to set the key more than once. |
117 | */ |
118 | key_set = !os_atomic_cmpxchg(&local_signing_key_set, false, true, relaxed); |
119 | |
120 | if (key_set) { |
121 | panic("attempted to set the local signing public key multiple times" ); |
122 | } |
123 | |
124 | memcpy(dst: local_signing_public_key, src: public_key, n: sizeof(local_signing_public_key)); |
125 | } |
126 | |
127 | uint8_t* |
128 | xnu_get_local_signing_public_key(void) |
129 | { |
130 | bool key_set = os_atomic_load(&local_signing_key_set, relaxed); |
131 | |
132 | if (key_set) { |
133 | return local_signing_public_key; |
134 | } |
135 | |
136 | return NULL; |
137 | } |
138 | |
139 | #pragma mark Image4 |
140 | |
141 | static uint8_t __attribute__((aligned(8))) |
142 | _xnu_image4_storage[IMG4_PMAP_DATA_SIZE_RECOMMENDED] = {0}; |
143 | |
144 | void* |
145 | xnu_image4_storage_data( |
146 | size_t *allocated_size) |
147 | { |
148 | if (allocated_size) { |
149 | *allocated_size = sizeof(_xnu_image4_storage); |
150 | } |
151 | return _xnu_image4_storage; |
152 | } |
153 | |
154 | void |
155 | xnu_image4_set_nonce( |
156 | const img4_nonce_domain_index_t ndi, |
157 | const img4_nonce_t *nonce) |
158 | { |
159 | /* |
160 | * As a hold over from legacy code, AppleImage4 only ever manages nonces |
161 | * from the kernel interface through the PMAP_CS runtime. So even though |
162 | * we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime. |
163 | */ |
164 | |
165 | IMG4_RUNTIME_PMAP_CS->i4rt_set_nonce( |
166 | IMG4_RUNTIME_PMAP_CS, |
167 | ndi, |
168 | nonce); |
169 | } |
170 | |
171 | void |
172 | xnu_image4_roll_nonce( |
173 | const img4_nonce_domain_index_t ndi) |
174 | { |
175 | /* |
176 | * As a hold over from legacy code, AppleImage4 only ever manages nonces |
177 | * from the kernel interface through the PMAP_CS runtime. So even though |
178 | * we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime. |
179 | */ |
180 | |
181 | IMG4_RUNTIME_PMAP_CS->i4rt_roll_nonce( |
182 | IMG4_RUNTIME_PMAP_CS, |
183 | ndi); |
184 | } |
185 | |
186 | errno_t |
187 | xnu_image4_copy_nonce( |
188 | const img4_nonce_domain_index_t ndi, |
189 | img4_nonce_t *nonce_out) |
190 | { |
191 | errno_t ret = EPERM; |
192 | |
193 | /* |
194 | * As a hold over from legacy code, AppleImage4 only ever manages nonces |
195 | * from the kernel interface through the PMAP_CS runtime. So even though |
196 | * we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime. |
197 | */ |
198 | |
199 | ret = IMG4_RUNTIME_PMAP_CS->i4rt_copy_nonce( |
200 | IMG4_RUNTIME_PMAP_CS, |
201 | ndi, |
202 | nonce_out); |
203 | |
204 | if (ret != 0) { |
205 | printf("unable to copy image4 nonce: %llu | %d\n" , ndi, ret); |
206 | } |
207 | |
208 | return ret; |
209 | } |
210 | |
211 | errno_t |
212 | xnu_image4_execute_object( |
213 | img4_runtime_object_spec_index_t obj_spec_index, |
214 | const img4_buff_t *payload, |
215 | const img4_buff_t *manifest) |
216 | { |
217 | errno_t ret = EPERM; |
218 | const img4_runtime_object_spec_t *obj_spec = NULL; |
219 | |
220 | obj_spec = image4_get_object_spec_from_index(obj_spec_index); |
221 | if (obj_spec == NULL) { |
222 | return ENOENT; |
223 | } |
224 | |
225 | /* |
226 | * As a hold over from legacy code, AppleImage4 only ever executes objects |
227 | * through the kernel interface through the PMAP_CS runtime. So even though |
228 | * we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime. |
229 | */ |
230 | |
231 | ret = img4_runtime_execute_object( |
232 | IMG4_RUNTIME_PMAP_CS, |
233 | obj_spec, |
234 | payload, |
235 | manifest); |
236 | |
237 | if (ret != 0) { |
238 | printf("unable to execute image4 object: %d\n" , ret); |
239 | } |
240 | |
241 | return ret; |
242 | } |
243 | |
244 | errno_t |
245 | xnu_image4_copy_object( |
246 | img4_runtime_object_spec_index_t obj_spec_index, |
247 | vm_address_t object_out, |
248 | size_t *object_length) |
249 | { |
250 | errno_t ret = EPERM; |
251 | img4_buff_t object_payload = IMG4_BUFF_INIT; |
252 | size_t object_payload_length = 0; |
253 | const img4_runtime_object_spec_t *obj_spec = NULL; |
254 | |
255 | obj_spec = image4_get_object_spec_from_index(obj_spec_index); |
256 | if (obj_spec == NULL) { |
257 | return ENOENT; |
258 | } |
259 | |
260 | /* |
261 | * The object length is used as an in/out parameter, so we require that this parameter |
262 | * is used to specify the length of the buffer. |
263 | */ |
264 | object_payload_length = *object_length; |
265 | |
266 | object_payload.i4b_bytes = (void*)object_out; |
267 | object_payload.i4b_len = object_payload_length; |
268 | |
269 | /* |
270 | * As a hold over from legacy code, AppleImage4 only ever copies objects |
271 | * through the kernel interface through the PMAP_CS runtime. So even though |
272 | * we don't have a PMAP_CS monitor, we still pass in the PMAP_CS runtime. |
273 | */ |
274 | |
275 | ret = img4_runtime_copy_object( |
276 | IMG4_RUNTIME_PMAP_CS, |
277 | obj_spec, |
278 | &object_payload, |
279 | &object_payload_length); |
280 | if (ret != 0) { |
281 | printf("unable to copy image4 object: %d\n" , ret); |
282 | } |
283 | |
284 | /* Update the length with what we received from the image4 runtime */ |
285 | *object_length = object_payload_length; |
286 | |
287 | return ret; |
288 | } |
289 | |
290 | const void* |
291 | xnu_image4_get_monitor_exports(void) |
292 | { |
293 | printf("monitor exports not supported without a monitor\n" ); |
294 | return NULL; |
295 | } |
296 | |
297 | errno_t |
298 | xnu_image4_set_release_type( |
299 | __unused const char *release_type) |
300 | { |
301 | /* |
302 | * We don't need to inform the monitor about the release type when there |
303 | * is no monitor environment available. |
304 | */ |
305 | |
306 | printf("explicit release-type-set not supported without a monitor\n" ); |
307 | return ENOTSUP; |
308 | } |
309 | |
310 | errno_t |
311 | xnu_image4_set_bnch_shadow( |
312 | __unused const img4_nonce_domain_index_t ndi) |
313 | { |
314 | /* |
315 | * We don't need to inform the monitor about the BNCH shadow when there |
316 | * is no monitor environment available. |
317 | */ |
318 | |
319 | printf("explicit BNCH-shadow-set not supported without a monitor\n" ); |
320 | return ENOTSUP; |
321 | } |
322 | |
323 | #pragma mark Image4 - New |
324 | |
325 | kern_return_t |
326 | xnu_image4_transfer_region( |
327 | image4_cs_trap_t selector, |
328 | __unused vm_address_t region_addr, |
329 | __unused vm_size_t region_size) |
330 | { |
331 | panic("image4 dispatch: transfer without code signing monitor: %llu" , selector); |
332 | } |
333 | |
334 | kern_return_t |
335 | xnu_image4_reclaim_region( |
336 | image4_cs_trap_t selector, |
337 | __unused vm_address_t region_addr, |
338 | __unused vm_size_t region_size) |
339 | { |
340 | panic("image4 dispatch: reclaim without code signing monitor: %llu" , selector); |
341 | } |
342 | |
343 | errno_t |
344 | xnu_image4_monitor_trap( |
345 | image4_cs_trap_t selector, |
346 | __unused const void *input_data, |
347 | __unused size_t input_size) |
348 | { |
349 | panic("image4 dispatch: trap without code signing monitor: %llu" , selector); |
350 | } |
351 | |
352 | #endif /* !CODE_SIGNING_MONITOR */ |
353 | |