1 /*
2 * Copyright (c) 2019-2020 Apple 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 <mach/mach_types.h>
30 #include <kern/kern_types.h>
31 #include <mach/notify.h>
32 #include <mach/resource_monitors.h>
33 #include <os/log.h>
34
35 #include <mach/host_special_ports.h>
36 #include <mach/mach_host_server.h>
37 #include <mach/host_priv_server.h>
38 #include <mach/fairplayd_notification.h>
39 #include <mach/arcade_upcall.h>
40
41 #include <kern/kern_types.h>
42 #include <kern/assert.h>
43 #include <kern/host.h>
44 #include <kern/ast.h>
45 #include <kern/task.h>
46
47 #include <kern/arcade.h>
48 #include <mach/arcade_register_server.h>
49
50 #include <IOKit/IOBSD.h>
51
52 #if !defined(MAXPATHLEN)
53 #define MAXPATHLEN 4096
54 #endif
55
56 extern struct proc *current_proc(void);
57 extern int proc_pidpathinfo_internal(struct proc *p, uint64_t arg,
58 char *buffer, uint32_t buffersize,
59 int32_t *retval);
60 extern off_t proc_getexecutableoffset(struct proc *p);
61
62 /*
63 * Simple structure to represent a handle for the Arcade registration.
64 *
65 * This registration is done with an independent kobject callback, rather
66 * than a reply, so that we execute it in the context of the user-space
67 * server replying (in order to do an entitlement check on the reply).
68 *
69 * We cache the resulting upcall port until it fails, and then we go
70 * get another one.
71 */
72 struct arcade_register {
73 ipc_port_t ar_port;
74 };
75 typedef struct arcade_register *arcade_register_t;
76
77 IPC_KOBJECT_DEFINE(IKOT_ARCADE_REG,
78 .iko_op_movable_send = true,
79 .iko_op_stable = true,
80 .iko_op_permanent = true);
81
82 static SECURITY_READ_ONLY_LATE(struct arcade_register) arcade_register_global;
83
84 void
arcade_prepare(task_t task,thread_t thread)85 arcade_prepare(task_t task, thread_t thread)
86 {
87 /* Platform binaries are exempt */
88 if (task_get_platform_binary(task)) {
89 return;
90 }
91
92 /* Check to see if the task has the arcade entitlement */
93 if (!IOTaskHasEntitlement(task, "com.apple.developer.arcade-operations")) {
94 return;
95 }
96
97 /* Others will stop in the AST to make an upcall */
98 thread_ast_set(thread, AST_ARCADE);
99 }
100
101 static LCK_GRP_DECLARE(arcade_upcall_lck_grp, "arcade_upcall");
102 static LCK_MTX_DECLARE(arcade_upcall_mutex, &arcade_upcall_lck_grp);
103
104 static ipc_port_t arcade_upcall_port = IP_NULL;
105 static boolean_t arcade_upcall_refresh_in_progress = FALSE;
106 static boolean_t arcade_upcall_refresh_waiters = FALSE;
107
108 void
arcade_init(void)109 arcade_init(void)
110 {
111 ipc_port_t port;
112
113 /* Initialize the global arcade_register kobject and associated port */
114 port = ipc_kobject_alloc_port((ipc_kobject_t)&arcade_register_global,
115 IKOT_ARCADE_REG, IPC_KOBJECT_ALLOC_MAKE_SEND);
116 os_atomic_store(&arcade_register_global.ar_port, port, release);
117 }
118
119 arcade_register_t
convert_port_to_arcade_register(ipc_port_t port)120 convert_port_to_arcade_register(
121 ipc_port_t port)
122 {
123 arcade_register_t arcade_reg = ARCADE_REG_NULL;
124
125 if (IP_VALID(port)) {
126 arcade_reg = ipc_kobject_get_stable(port, IKOT_ARCADE_REG);
127 if (arcade_reg) {
128 assert(arcade_reg == &arcade_register_global);
129 assert(arcade_reg->ar_port == port);
130 }
131 }
132 return arcade_reg;
133 }
134
135 ipc_port_t
convert_arcade_register_to_port(arcade_register_t arcade_reg)136 convert_arcade_register_to_port(
137 arcade_register_t arcade_reg)
138 {
139 ipc_port_t port = IP_NULL;
140
141 if (arcade_reg == &arcade_register_global) {
142 port = arcade_reg->ar_port;
143 }
144 return port;
145 }
146
147 kern_return_t
arcade_register_new_upcall(arcade_register_t arcade_reg,mach_port_t port)148 arcade_register_new_upcall(
149 arcade_register_t arcade_reg,
150 mach_port_t port)
151 {
152 os_log(OS_LOG_DEFAULT, "arcade: received register request");
153 if (arcade_reg == ARCADE_REG_NULL) {
154 return KERN_INVALID_ARGUMENT;
155 }
156 assert(arcade_reg == &arcade_register_global);
157
158 /* Check to see if this is the real arcade subscription service */
159 if (!IOCurrentTaskHasEntitlement("com.apple.arcade.fpsd")) {
160 return KERN_INVALID_VALUE;
161 }
162
163 lck_mtx_lock(&arcade_upcall_mutex);
164
165 if (arcade_upcall_refresh_in_progress) {
166 /* If we have an old arcade upcall port, discard it */
167 if (IP_VALID(arcade_upcall_port)) {
168 ipc_port_release_send(arcade_upcall_port);
169 arcade_upcall_port = IP_NULL;
170 }
171 arcade_upcall_port = port; /* owns send right */
172
173 /* Wake up anyone waiting for the update */
174 lck_mtx_unlock(&arcade_upcall_mutex);
175 thread_wakeup(&arcade_upcall_port);
176 return KERN_SUCCESS;
177 }
178 lck_mtx_unlock(&arcade_upcall_mutex);
179 return KERN_FAILURE;
180 }
181
182
183 static kern_return_t
arcade_upcall_refresh(uint64_t deadline)184 arcade_upcall_refresh(uint64_t deadline)
185 {
186 ipc_port_t fairplayd_port = IP_NULL;
187 wait_result_t wr = THREAD_NOT_WAITING;
188 kern_return_t kr;
189
190 LCK_MTX_ASSERT(&arcade_upcall_mutex, LCK_MTX_ASSERT_OWNED);
191
192 /* If someone else is doing the update, wait for them */
193 if (arcade_upcall_refresh_in_progress) {
194 arcade_upcall_refresh_waiters = TRUE;
195 wr = lck_mtx_sleep(&arcade_upcall_mutex, LCK_SLEEP_DEFAULT,
196 &arcade_upcall_refresh_in_progress, THREAD_INTERRUPTIBLE);
197 goto out;
198 }
199
200 arcade_upcall_refresh_in_progress = TRUE;
201
202 /* If we have an old arcade upcall port, discard it */
203 if (IP_VALID(arcade_upcall_port)) {
204 ipc_port_release_send(arcade_upcall_port);
205 arcade_upcall_port = IP_NULL;
206 }
207
208 if (host_get_fairplayd_port(host_priv_self(), &fairplayd_port) != KERN_SUCCESS) {
209 panic("arcade_upcall_refresh(get fairplayd)");
210 }
211
212 /* If no valid fairplayd port registered, we're done */
213 if (!IP_VALID(fairplayd_port)) {
214 goto finish_in_progress;
215 }
216
217 /*
218 * Send a fairplayd notification to request a new arcade upcall port.
219 * Pass along a send right to the arcade_register kobject to complete
220 * the registration.
221 */
222 ipc_port_t port = convert_arcade_register_to_port(&arcade_register_global);
223 kr = fairplayd_arcade_request(fairplayd_port, port);
224
225 ipc_port_release_send(fairplayd_port);
226
227 switch (kr) {
228 case MACH_MSG_SUCCESS:
229 break;
230 default:
231 goto finish_in_progress;
232 }
233
234 /*
235 * Wait on the arcade upcall port to get registered through the
236 * registration kobject waiting with a deadline here.
237 */
238 wr = lck_mtx_sleep_deadline(&arcade_upcall_mutex, LCK_SLEEP_DEFAULT,
239 &arcade_upcall_port, THREAD_INTERRUPTIBLE, deadline);
240
241 finish_in_progress:
242 arcade_upcall_refresh_in_progress = FALSE;
243
244 /* Wakeup any waiters */
245 if (arcade_upcall_refresh_waiters) {
246 arcade_upcall_refresh_waiters = FALSE;
247 thread_wakeup_with_result(&arcade_upcall_refresh_in_progress, wr);
248 }
249
250 out:
251 switch (wr) {
252 case THREAD_AWAKENED:
253 return KERN_SUCCESS;
254 default:
255 return KERN_FAILURE;
256 }
257 }
258
259 static kern_return_t
__MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(mach_port_t port,vm_map_copy_t path,vm_size_t pathlen,off_t offset,boolean_t * should_killp)260 __MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(mach_port_t port,
261 vm_map_copy_t path,
262 vm_size_t pathlen,
263 off_t offset,
264 boolean_t *should_killp)
265 {
266 mach_msg_type_number_t len = (mach_msg_type_number_t)pathlen;
267 return arcade_upcall(port, (vm_offset_t)path, len, offset, should_killp);
268 }
269
270 void
arcade_ast(__unused thread_t thread)271 arcade_ast(__unused thread_t thread)
272 {
273 ipc_port_t port;
274 uint64_t deadline;
275 kern_return_t kr;
276 int retval;
277
278 /* Determine the deadline */
279 clock_interval_to_deadline(10, NSEC_PER_SEC, &deadline);
280
281 restart:
282 lck_mtx_lock(&arcade_upcall_mutex);
283 port = ipc_port_copy_send_mqueue(arcade_upcall_port);
284 /*
285 * if the arcade_upcall_port was inactive, "port" will be IP_DEAD.
286 * Otherwise, it holds a send right to the arcade_upcall_port.
287 */
288
289 while (!IP_VALID(port)) {
290 /*
291 * Refresh the arcade upcall port. If that gives up,
292 * give up ourselves.
293 */
294 kr = arcade_upcall_refresh(deadline);
295 if (kr != KERN_SUCCESS) {
296 lck_mtx_unlock(&arcade_upcall_mutex);
297 goto fail;
298 }
299 port = ipc_port_copy_send_mqueue(arcade_upcall_port);
300 }
301 lck_mtx_unlock(&arcade_upcall_mutex);
302
303 /* We have an upcall port send right */
304
305 /* Gather the data we need to send in the upcall */
306 off_t offset;
307 struct proc *p = current_proc();
308 char *path;
309 vm_map_copy_t copy;
310
311 path = kalloc_data(MAXPATHLEN, Z_WAITOK | Z_ZERO);
312 retval = proc_pidpathinfo_internal(p, 0, path, MAXPATHLEN, NULL);
313 assert(!retval);
314 kr = vm_map_copyin(kernel_map, (vm_map_address_t)path, MAXPATHLEN, FALSE, ©);
315 assert(kr == KERN_SUCCESS);
316 kfree_data(path, MAXPATHLEN);
317
318 offset = proc_getexecutableoffset(p);
319
320 /* MAKE THE UPCALL */
321 boolean_t should_kill = TRUE;
322 kr = __MAKING_UPCALL_TO_ARCADE_VALIDATION_SERVICE__(port, copy, MAXPATHLEN, offset, &should_kill);
323 os_log(OS_LOG_DEFAULT, "arcade: subscription validation upcall returned %#x", kr);
324 ipc_port_release_send(port);
325
326 switch (kr) {
327 case MACH_SEND_INVALID_DEST:
328 vm_map_copy_discard(copy);
329 OS_FALLTHROUGH;
330 case MIG_SERVER_DIED:
331 goto restart;
332 case KERN_SUCCESS:
333 if (should_kill == TRUE) {
334 /*
335 * Invalid subscription. UI already presented as to why it did not
336 * launch.
337 */
338 task_terminate_internal(current_task());
339 }
340 break;
341 default:
342 fail:
343 /*
344 * Failure of the subscription validation mechanism, not a rejection.
345 * for a missing subscription. There will be no indication WHY this
346 * process didn't launch. We might want this to be an exit_with_reason()
347 * in the future.
348 */
349 os_log(OS_LOG_DEFAULT, "arcade: unable to make subscription upcall, error %#x", kr);
350 task_terminate_internal(current_task());
351 break;
352 }
353 }
354