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